diff --git a/.clang-format b/.clang-format deleted file mode 100644 index 400349f15..000000000 --- a/.clang-format +++ /dev/null @@ -1,75 +0,0 @@ ---- -BasedOnStyle: LLVM -BreakBeforeBraces: Stroustrup -IndentWidth: 4 -TabWidth: 4 -AlignAfterOpenBracket: AlwaysBreak -AlignConsecutiveMacros: 'true' -AlignConsecutiveAssignments: 'false' -AlignConsecutiveDeclarations: 'false' -AlignEscapedNewlines: Right -AlignOperands: 'true' -AlignTrailingComments: 'true' -AllowAllArgumentsOnNextLine: 'false' -AllowAllConstructorInitializersOnNextLine: 'false' -AllowAllParametersOfDeclarationOnNextLine: 'false' -AllowShortBlocksOnASingleLine: 'true' -AllowShortCaseLabelsOnASingleLine: 'false' -AllowShortFunctionsOnASingleLine: None -AllowShortIfStatementsOnASingleLine: Never -AlwaysBreakAfterReturnType: None -BinPackArguments: 'false' -BinPackParameters: 'false' -BreakBeforeBinaryOperators: NonAssignment -BreakBeforeTernaryOperators: 'true' -BreakConstructorInitializers: BeforeComma -BreakInheritanceList: BeforeComma -CompactNamespaces: 'false' -ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' -ConstructorInitializerIndentWidth: '4' -ContinuationIndentWidth: '4' -Cpp11BracedListStyle: 'false' -FixNamespaceComments: 'true' -IncludeBlocks: Regroup -IndentCaseLabels: 'true' -IndentPPDirectives: None -IndentWrappedFunctionNames: 'false' -KeepEmptyLinesAtTheStartOfBlocks: 'false' -MaxEmptyLinesToKeep: '1' -NamespaceIndentation: None -PointerAlignment: Left -ReflowComments: 'true' -SortIncludes: 'true' -SortUsingDeclarations: 'true' -SpaceAfterCStyleCast: 'false' -SpaceAfterLogicalNot: 'true' -SpaceAfterTemplateKeyword: 'true' -SpaceBeforeAssignmentOperators: 'true' -SpaceBeforeCpp11BracedList: 'true' -SpaceBeforeCtorInitializerColon: 'true' -SpaceBeforeInheritanceColon: 'true' -SpaceBeforeParens: ControlStatements -SpaceBeforeRangeBasedForLoopColon: 'true' -SpaceInEmptyParentheses: 'false' -SpacesBeforeTrailingComments: '3' -SpacesInAngles: 'false' -SpacesInCStyleCastParentheses: 'false' -SpacesInContainerLiterals: 'true' -SpacesInParentheses: 'false' -SpacesInSquareBrackets: 'false' -UseTab: 'Always' - ---- -Language: Cpp -Standard: Cpp03 -ColumnLimit: '240' ---- -Language: ObjC -ColumnLimit: '240' ---- -Language: Java -ColumnLimit: '240' ---- -Language: CSharp -ColumnLimit: '240' -... diff --git a/.clangd b/.clangd deleted file mode 100644 index 0605ccdb7..000000000 --- a/.clangd +++ /dev/null @@ -1,6 +0,0 @@ -CompileFlags: - Add: - - "-std=c++17" - - "-I../ext" - - "-I../ext/prometheus-cpp-lite-1.0/core/include" - - "-I../ext/prometheus-cpp-lite-1.0/simpleapi/include" diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 4acef401c..000000000 --- a/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -.git/ -workspace/ diff --git a/.drone.jsonnet b/.drone.jsonnet deleted file mode 100644 index 7daef37fe..000000000 --- a/.drone.jsonnet +++ /dev/null @@ -1,256 +0,0 @@ -// -// tweakables -// - -local registry = "084037375216.dkr.ecr.us-east-2.amazonaws.com"; -local build_channel = "zerotier-builds"; -local release_channel = "zerotier-releases"; - -local targets = [ - { "os": "linux", distro: "redhat", "name": "el9", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag", "custom" ] }, - { "os": "linux", distro: "redhat", "name": "el8", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] }, - { "os": "linux", distro: "redhat", "name": "el7", "isas": [ "386", "amd64", "ppc64le"], "events": [ "tag" ] }, - { "os": "linux", distro: "amazon", "name": "amzn2", "isas": [ "amd64", "arm64" ], "events": [ "tag" ] }, - { "os": "linux", distro: "amazon", "name": "amzn2022", "isas": [ "amd64", "arm64" ], "events": [ "tag" ] }, - { "os": "linux", distro: "fedora", "name": "fc38", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] }, - { "os": "linux", distro: "fedora", "name": "fc37", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] }, - { "os": "linux", distro: "fedora", "name": "fc36", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] }, - { "os": "linux", distro: "ubuntu", "name": "jammy", "isas": [ "armv7", "amd64", "arm64", "ppc64le", "s390x", "riscv64" ], "events": [ "tag" ] }, - { "os": "linux", distro: "ubuntu", "name": "focal", "isas": [ "armv7", "amd64", "arm64", "ppc64le", "s390x", "riscv64" ], "events": [ "tag" ] }, - { "os": "linux", distro: "ubuntu", "name": "bionic", "isas": [ "386", "armv7", "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] }, - { "os": "linux", distro: "ubuntu", "name": "xenial", "isas": [ "386", "armv7", "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "tag" ] }, - { "os": "linux", distro: "ubuntu", "name": "trusty", "isas": [ "386", "armv7", "amd64", "arm64" ], "events": [ "tag" ] }, - { "os": "linux", distro: "debian", "name": "bookworm", "isas": [ "386", "armv7", "amd64", "arm64", "mips64le", "ppc64le", "s390x" ], "events": [ "tag"] }, - { "os": "linux", distro: "debian", "name": "bullseye", "isas": [ "386", "armv7", "amd64", "arm64", "mips64le", "ppc64le", "s390x" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "debian", "name": "buster", "isas": [ "386", "armv7", "amd64", "arm64" ], "events": [ "tag" ] }, - { "os": "linux", distro: "debian", "name": "stretch", "isas": [ "386", "armv7", "amd64", "arm64" ], "events": [ "tag" ] }, - { "os": "linux", distro: "debian", "name": "jessie", "isas": [ "386", "armv7", "amd64" ], "events": [ "tag" ] }, - -// { "os": "windows", distro: "windows", "name": "windows", "isas": [ "amd64" ], "events": [ "push", "tag", "custom" ] }, -// { "os": "darwin", distro: "darwin", "name": "darwin", "isas": [ "amd64" ], "events": [ "push", "tag", "custom" ] }, - -]; - -local less_targets = [ - { "os": "linux", distro: "redhat", "name": "el9", "isas": [ "amd64", "arm64" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "redhat", "name": "el8", "isas": [ "amd64", "arm64" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "ubuntu", "name": "jammy", "isas": [ "armv7", "amd64", "arm64" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "ubuntu", "name": "focal", "isas": [ "armv7", "amd64", "arm64" ], "events": [ "push", "tag", "custom" ] }, -]; - - -local native_targets = [ - { "os": "linux", distro: "debian", "name": "bullseye", "isas": [ "386", "armv7", "amd64", "arm64" ], "events": [ "push", "tag", "custom" ] }, -]; - -local master_targets = [ - // - // copypasta from here - // - { "os": "linux", distro: "redhat", "name": "el9", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "redhat", "name": "el8", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "redhat", "name": "el7", "isas": [ "386", "amd64", "ppc64le"], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "amazon", "name": "amzn2", "isas": [ "amd64", "arm64" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "amazon", "name": "amzn2022", "isas": [ "amd64", "arm64" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "fedora", "name": "fc38", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "fedora", "name": "fc37", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "fedora", "name": "fc36", "isas": [ "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "ubuntu", "name": "jammy", "isas": [ "armv7", "amd64", "arm64", "ppc64le", "s390x", "riscv64" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "ubuntu", "name": "focal", "isas": [ "armv7", "amd64", "arm64", "ppc64le", "s390x", "riscv64" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "ubuntu", "name": "bionic", "isas": [ "386", "armv7", "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "ubuntu", "name": "xenial", "isas": [ "386", "armv7", "amd64", "arm64", "ppc64le", "s390x" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "ubuntu", "name": "trusty", "isas": [ "386", "armv7", "amd64", "arm64" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "debian", "name": "sid", "isas": [ "386", "armv7", "amd64", "arm64", "mips64le", "ppc64le", "s390x", "riscv64" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "debian", "name": "bookworm", "isas": [ "386", "armv7", "amd64", "arm64", "mips64le", "ppc64le", "s390x" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "debian", "name": "bullseye", "isas": [ "386", "armv7", "amd64", "arm64", "mips64le", "ppc64le", "s390x" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "debian", "name": "buster", "isas": [ "386", "armv7", "amd64", "arm64" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "debian", "name": "stretch", "isas": [ "386", "armv7", "amd64", "arm64" ], "events": [ "push", "tag", "custom" ] }, - { "os": "linux", distro: "debian", "name": "jessie", "isas": [ "386", "armv7", "amd64" ], "events": [ "push", "tag", "custom" ] }, - { "os": "windows", distro: "windows", "name": "win2k22", "isas": [ "amd64" ], "events": [ "push", "tag", "custom" ] } -]; - -// -// functions -// - -local pipeline_type(os) = if os == "darwin" then "exec" else "docker"; -local builder_image(os) = if os == "linux" then registry + "/honda-builder" else registry + "/windows-builder"; -local tester_image(os) = if os == "linux" then registry + "/honda-builder" else registry + "/windows-tester"; -local build_step_volumes(os) = if os == "linux" then [ { name: "zerotier-builds", path: "/zerotier-builds" } ] else []; -local release_step_volumes(os) = if os == "linux" then [ { name: "zerotier-releases", path: "/zerotier-releases" } ] else []; -local host_volumes(os) = if os == "linux" then [ - { name: "zerotier-builds", host: { path: "/zerotier-builds" } }, - { name: "zerotier-releases", host: { path: "/zerotier-releases" } }, -] else []; - -local index_image(distro) = - if distro == "debian" || distro == "ubuntu" then - registry + "/apt-builder" - else if distro == "redhat" || distro == "fedora" || distro == "amazon" then - registry + "/dnf-builder" - else if distro == "windows" then - registry + "/msi-builder" -; - -local copy_commands(os, distro, name, isa, version) = - if os == "linux" then [ - std.join(" ", [ "./ci/scripts/publish.sh", name, distro, isa, version, "${DRONE_BUILD_EVENT}" ]) - ] - else if os == "windows" then [ - "C:\\scripts\\fix-ec2-metadata.ps1", - "Get-ChildItem windows", - // "aws s3 cp windows\\bytey-SetupFiles\\bytey.msi s3://zerotier-builds/windows/" + version + "/bytey.msi", - ] else if os == "darwin" then [ - "echo hello" - ] -; - -local index_commands(os, channel, distro, name, isas) = - if os == "linux" then - [ "/usr/local/bin/index " + channel + " " + distro + " " + name + " " + std.join(" ", isas) ] - else if os == "windows" then - [ "Get-ChildItem -Recurse windows" ] -; - -local build_commands(os, distro, name, isa, version) = - if os == "linux" then - [ std.join(" ", [ "./ci/scripts/build.sh", name, distro, isa, version, "${DRONE_BUILD_EVENT}" ]) ] - else - if os == "windows" then - [ "windows/build.ps1", "windows/package.ps1" ] - else - if os == "darwin" then - [ "whoami" ] -; - -local test_commands(os, distro, name, isa, version) = - if os == "linux" then - [ std.join(" ", [ "./ci/scripts/test.sh", name, distro, isa, version, "${DRONE_BUILD_EVENT}" ]) ] - else - if os == "windows" then - [ "windows/testpackage.ps1 " + version ] -; - -// -// render -// - -local Build(os, distro, name, isa, events) = { - "kind": "pipeline", - "type": pipeline_type(os), - "name": std.join(" ", [ name, isa, "build" ]), - "pull": "always", - "clone": { "depth": 1, [ if os == "darwin" then "disable" ]: true }, - "steps": [ - { - "name": "build", - "image": builder_image(os), - "commands": build_commands(os, distro, name, isa, "100.0.0+${DRONE_COMMIT_SHA:0:8}"), - "when": { "event": [ "push" ]}, - }, - { - "name": "release", - "image": builder_image(os), - "commands": build_commands(os, distro, name, isa, "${DRONE_TAG}"), - "when": { "event": [ "tag" ]}, - }, - { - "name": "copy build", - "image": builder_image(os), - "commands": copy_commands(os, distro, name, isa, "100.0.0+${DRONE_COMMIT_SHA:0:8}"), - "volumes": build_step_volumes(os), - "when": { "event": [ "push" ]}, - }, - { - "name": "copy relase", - "image": builder_image(os), - "commands": copy_commands(os, distro, name, isa, "${DRONE_TAG}"), - "volumes": release_step_volumes(os), - "when": { "event": [ "tag" ]}, - }, - ], - "volumes": host_volumes(os), - "platform": { "os": os, [ if isa == "arm64" || isa == "armv7" then "arch" ]: "arm64" }, - "trigger": { "event": events } -}; - -local Test(os, distro, name, isa, events) = { - "kind": "pipeline", - "type": pipeline_type(os), - "name": std.join(" ", [ name, isa, "test"]), - "pull": "always", - "clone": { "depth": 1 }, - "steps": [ - { - "name": "test build", - "image": tester_image(os), - "volumes": build_step_volumes(os), - "commands": test_commands(os, distro, name, isa, "100.0.0+${DRONE_COMMIT_SHA:0:8}"), - "when": { "event": [ "push" ]}, - }, - { - "name": "test release", - "image": tester_image(os), - "volumes": release_step_volumes(os), - "commands": test_commands(os, distro, name, isa, "${DRONE_TAG}"), - "when": { "event": [ "tag" ]}, - }, - ], - "volumes": host_volumes(os), - "platform": { "os": os, [ if isa == "arm64" || isa == "armv7" then "arch" ]: "arm64" }, - "depends_on": [ std.join(" ", [ name, "index" ]) ], - "trigger": { "event": events } -}; - -local Index(p) = { - "kind": "pipeline", - "type": pipeline_type(p.os), - "name": std.join(" ", [ p.name, "index" ]), - "pull": "always", - "clone": { "depth": 1 }, - "steps": [ - { - "name": "index build", - "image": index_image(p.distro), - "commands": index_commands(p.os, "zerotier-builds", p.distro, p.name, p.isas), - "volumes": build_step_volumes(p.os), - "environment":{ "GPG_PRIVATE_KEY": { from_secret: "gpg-private-key" }}, - "when": { "event": [ "push" ]}, - }, - { - "name": "index release", - "image": index_image(p.distro), - "commands": index_commands(p.os, "zerotier-releases", p.distro, p.name, p.isas), - "volumes": release_step_volumes(p.os), - "environment":{ "GPG_PRIVATE_KEY": { from_secret: "gpg-private-key" }}, - "when": { "event": [ "tag" ]}, - }, - ], - "volumes": host_volumes(p.os), - "platform": { "os": p.os }, - depends_on: std.flattenArrays([ [ std.join(" ", [ p.name, isa, "build" ]) ] for isa in p.isas ]), - "trigger": { "event": p.events } -}; - -// -// print -// - -std.flattenArrays([ - [ - Build(p.os, p.distro, p.name, isa, p.events) - for isa in p.isas - ] + - [ - Index(p) - ] - for p in native_targets - ]) + - std.flattenArrays([ - [ - Test(p.os, p.distro, p.name, isa, p.events) - for isa in p.isas - ] - for p in native_targets - ]) - \ No newline at end of file diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index b837b1b21..000000000 --- a/.drone.yml +++ /dev/null @@ -1,465 +0,0 @@ ---- -clone: - depth: 1 -kind: pipeline -name: bullseye 386 build -platform: - os: linux -pull: always -steps: -- commands: - - ./ci/scripts/build.sh bullseye debian 386 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: build - when: - event: - - push -- commands: - - ./ci/scripts/build.sh bullseye debian 386 ${DRONE_TAG} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: release - when: - event: - - tag -- commands: - - ./ci/scripts/publish.sh bullseye debian 386 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: copy build - volumes: - - name: zerotier-builds - path: /zerotier-builds - when: - event: - - push -- commands: - - ./ci/scripts/publish.sh bullseye debian 386 ${DRONE_TAG} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: copy relase - volumes: - - name: zerotier-releases - path: /zerotier-releases - when: - event: - - tag -trigger: - event: - - push - - tag - - custom -type: docker -volumes: -- host: - path: /zerotier-builds - name: zerotier-builds -- host: - path: /zerotier-releases - name: zerotier-releases ---- -clone: - depth: 1 -kind: pipeline -name: bullseye armv7 build -platform: - arch: arm64 - os: linux -pull: always -steps: -- commands: - - ./ci/scripts/build.sh bullseye debian armv7 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: build - when: - event: - - push -- commands: - - ./ci/scripts/build.sh bullseye debian armv7 ${DRONE_TAG} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: release - when: - event: - - tag -- commands: - - ./ci/scripts/publish.sh bullseye debian armv7 100.0.0+${DRONE_COMMIT_SHA:0:8} - ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: copy build - volumes: - - name: zerotier-builds - path: /zerotier-builds - when: - event: - - push -- commands: - - ./ci/scripts/publish.sh bullseye debian armv7 ${DRONE_TAG} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: copy relase - volumes: - - name: zerotier-releases - path: /zerotier-releases - when: - event: - - tag -trigger: - event: - - push - - tag - - custom -type: docker -volumes: -- host: - path: /zerotier-builds - name: zerotier-builds -- host: - path: /zerotier-releases - name: zerotier-releases ---- -clone: - depth: 1 -kind: pipeline -name: bullseye amd64 build -platform: - os: linux -pull: always -steps: -- commands: - - ./ci/scripts/build.sh bullseye debian amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: build - when: - event: - - push -- commands: - - ./ci/scripts/build.sh bullseye debian amd64 ${DRONE_TAG} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: release - when: - event: - - tag -- commands: - - ./ci/scripts/publish.sh bullseye debian amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} - ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: copy build - volumes: - - name: zerotier-builds - path: /zerotier-builds - when: - event: - - push -- commands: - - ./ci/scripts/publish.sh bullseye debian amd64 ${DRONE_TAG} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: copy relase - volumes: - - name: zerotier-releases - path: /zerotier-releases - when: - event: - - tag -trigger: - event: - - push - - tag - - custom -type: docker -volumes: -- host: - path: /zerotier-builds - name: zerotier-builds -- host: - path: /zerotier-releases - name: zerotier-releases ---- -clone: - depth: 1 -kind: pipeline -name: bullseye arm64 build -platform: - arch: arm64 - os: linux -pull: always -steps: -- commands: - - ./ci/scripts/build.sh bullseye debian arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: build - when: - event: - - push -- commands: - - ./ci/scripts/build.sh bullseye debian arm64 ${DRONE_TAG} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: release - when: - event: - - tag -- commands: - - ./ci/scripts/publish.sh bullseye debian arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} - ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: copy build - volumes: - - name: zerotier-builds - path: /zerotier-builds - when: - event: - - push -- commands: - - ./ci/scripts/publish.sh bullseye debian arm64 ${DRONE_TAG} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: copy relase - volumes: - - name: zerotier-releases - path: /zerotier-releases - when: - event: - - tag -trigger: - event: - - push - - tag - - custom -type: docker -volumes: -- host: - path: /zerotier-builds - name: zerotier-builds -- host: - path: /zerotier-releases - name: zerotier-releases ---- -clone: - depth: 1 -depends_on: -- bullseye 386 build -- bullseye armv7 build -- bullseye amd64 build -- bullseye arm64 build -kind: pipeline -name: bullseye index -platform: - os: linux -pull: always -steps: -- commands: - - /usr/local/bin/index zerotier-builds debian bullseye 386 armv7 amd64 arm64 - environment: - GPG_PRIVATE_KEY: - from_secret: gpg-private-key - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/apt-builder - name: index build - volumes: - - name: zerotier-builds - path: /zerotier-builds - when: - event: - - push -- commands: - - /usr/local/bin/index zerotier-releases debian bullseye 386 armv7 amd64 arm64 - environment: - GPG_PRIVATE_KEY: - from_secret: gpg-private-key - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/apt-builder - name: index release - volumes: - - name: zerotier-releases - path: /zerotier-releases - when: - event: - - tag -trigger: - event: - - push - - tag - - custom -type: docker -volumes: -- host: - path: /zerotier-builds - name: zerotier-builds -- host: - path: /zerotier-releases - name: zerotier-releases ---- -clone: - depth: 1 -depends_on: -- bullseye index -kind: pipeline -name: bullseye 386 test -platform: - os: linux -pull: always -steps: -- commands: - - ./ci/scripts/test.sh bullseye debian 386 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: test build - volumes: - - name: zerotier-builds - path: /zerotier-builds - when: - event: - - push -- commands: - - ./ci/scripts/test.sh bullseye debian 386 ${DRONE_TAG} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: test release - volumes: - - name: zerotier-releases - path: /zerotier-releases - when: - event: - - tag -trigger: - event: - - push - - tag - - custom -type: docker -volumes: -- host: - path: /zerotier-builds - name: zerotier-builds -- host: - path: /zerotier-releases - name: zerotier-releases ---- -clone: - depth: 1 -depends_on: -- bullseye index -kind: pipeline -name: bullseye armv7 test -platform: - arch: arm64 - os: linux -pull: always -steps: -- commands: - - ./ci/scripts/test.sh bullseye debian armv7 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: test build - volumes: - - name: zerotier-builds - path: /zerotier-builds - when: - event: - - push -- commands: - - ./ci/scripts/test.sh bullseye debian armv7 ${DRONE_TAG} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: test release - volumes: - - name: zerotier-releases - path: /zerotier-releases - when: - event: - - tag -trigger: - event: - - push - - tag - - custom -type: docker -volumes: -- host: - path: /zerotier-builds - name: zerotier-builds -- host: - path: /zerotier-releases - name: zerotier-releases ---- -clone: - depth: 1 -depends_on: -- bullseye index -kind: pipeline -name: bullseye amd64 test -platform: - os: linux -pull: always -steps: -- commands: - - ./ci/scripts/test.sh bullseye debian amd64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: test build - volumes: - - name: zerotier-builds - path: /zerotier-builds - when: - event: - - push -- commands: - - ./ci/scripts/test.sh bullseye debian amd64 ${DRONE_TAG} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: test release - volumes: - - name: zerotier-releases - path: /zerotier-releases - when: - event: - - tag -trigger: - event: - - push - - tag - - custom -type: docker -volumes: -- host: - path: /zerotier-builds - name: zerotier-builds -- host: - path: /zerotier-releases - name: zerotier-releases ---- -clone: - depth: 1 -depends_on: -- bullseye index -kind: pipeline -name: bullseye arm64 test -platform: - arch: arm64 - os: linux -pull: always -steps: -- commands: - - ./ci/scripts/test.sh bullseye debian arm64 100.0.0+${DRONE_COMMIT_SHA:0:8} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: test build - volumes: - - name: zerotier-builds - path: /zerotier-builds - when: - event: - - push -- commands: - - ./ci/scripts/test.sh bullseye debian arm64 ${DRONE_TAG} ${DRONE_BUILD_EVENT} - image: 084037375216.dkr.ecr.us-east-2.amazonaws.com/honda-builder - name: test release - volumes: - - name: zerotier-releases - path: /zerotier-releases - when: - event: - - tag -trigger: - event: - - push - - tag - - custom -type: docker -volumes: -- host: - path: /zerotier-builds - name: zerotier-builds -- host: - path: /zerotier-releases - name: zerotier-releases ---- -kind: signature -hmac: 887a3ef78d3fe8f0149911e1e4876401dd7dd313b36eb893e791fa42f45d7768 - -... diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index dbe5330ba..000000000 --- a/.gitattributes +++ /dev/null @@ -1,4 +0,0 @@ -ext/bin/tap-windows-ndis6/x64/zttap300.inf eol=crlf -ext/bin/tap-windows-ndis6/x64.old/zttap300.inf eol=crlf -ext/bin/tap-windows-ndis6/x86/zttap300.inf eol=crlf -windows/TapDriver6/zttap300.inf eol=crlf diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..92a267422 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,46 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- +**Alternative, faster ways to get help** +If you have just started using ZeroTier, here are some places to get help: +- my.zerotier.com has a _Community_ tab. It's a live chat with other users and the developers. +- [ZeroTier Knowledge Base](https://zerotier.atlassian.net/wiki/spaces/SD/overview) +- www.zerotier.com has a Contact Us button +- email contact@zerotier.com + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Create a Network '...' +2. Install zerotier-one '....' +3. '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots or console output to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. Mac, Linux, Windows, BSD] + - OS/Distribution Version + - ZeroTier Version [e.g. 1.2.4] + - Hardware [e.g. raspberry pi 3] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Version [e.g. 1.2.4] + +**Additional context** +Add any other context about the problem here. +- ZeroTier Network Configuration +- Router Config +- Firewall Config (try turning the firewall off) +- General Network Environment: [ e.g Home, University Campus, Corporate LAN ] + diff --git a/.github/ISSUE_TEMPLATE/bugs-and-issues.md b/.github/ISSUE_TEMPLATE/bugs-and-issues.md deleted file mode 100644 index 92e6978c2..000000000 --- a/.github/ISSUE_TEMPLATE/bugs-and-issues.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: Bugs and Issues -about: Create a report to help us improve -title: '' -labels: NEEDS TRIAGE -assignees: '' - ---- - -# Before filing a Bug Report - -_Using these will ensure you get quicker support, and make this space available for code-related issues. Thank you!_ - -- [Docs Site](https://docs.zerotier.com/zerotier/troubleshooting) => Troubleshooting, quickstarts, and more advanced topics. -- [Discuss Forum](https://discuss.zerotier.com/) => Our discussion forum for users and support to mutually resolve issues & suggest ideas. -- [Reddit](https://www.reddit.com/r/zerotier/) => Our subreddit, which we monitor regularly and is fairly active. -- [Knowledge Base](https://zerotier.atlassian.net/wiki/spaces/SD/overview) => Older wiki. - -If you are having a connection issue, it's much easier to diagnose through the discussion forum or the ticket system. - - -# If you still want to file a Bug Report - -## Please let us know - -- What you expect to be happening. -- What is actually happening? -- Any steps to reproduce the error. -- Any relevant console output or screenshots. -- What operating system and ZeroTier version. Please try the latest ZeroTier release. - diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 5a1e094c3..066b2d920 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,13 +1,17 @@ --- name: Feature request about: Suggest an idea for this project -title: "[Feature Request] " -labels: suggestion -assignees: '' --- -If there is something you'd like to have added to ZeroTier, to go to https://discuss.zerotier.com/c/feature-requests/ instead. Issues there can be voted on and discussed in-depth. +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] +**Describe the solution you'd like** +A clear and concise description of what you want to happen. -Thank you! +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/game-connection-issue.md b/.github/ISSUE_TEMPLATE/game-connection-issue.md deleted file mode 100644 index d7b3a9b2d..000000000 --- a/.github/ISSUE_TEMPLATE/game-connection-issue.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: Game Connection Issue -about: Game issues are better served by forum posts -title: Please go to our Discuss or Reddit for game-related issues. Thanks! -labels: wontfix -assignees: '' - ---- - -Are you having trouble connecting to a game on your virtual network after installing ZeroTier? - -- [ ] Yes -- [ ] No - -If you answered yes, then it is very likely that your question would be better answered on our [Community Forums](https://discuss.zerotier.com) or [Reddit](https://www.reddit.com/r/zerotier/) community; we monitor both regularly. We also have extensive documentation on our [Knowledge Base](https://zerotier.atlassian.net/wiki/spaces/SD/overview). Thank you! diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index fee6dd338..000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,126 +0,0 @@ -on: - pull_request: - push: - workflow_dispatch: - -jobs: - build_ubuntu: - runs-on: ubuntu-latest - steps: - - name: gitconfig - run: | - git config --global core.autocrlf input - # git config --global core.eol lf - - name: checkout - uses: actions/checkout@v4 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - targets: x86_64-unknown-linux-gnu - components: rustfmt, clippy - - - name: Set up cargo cache - uses: Swatinem/rust-cache@v2 - continue-on-error: false - with: - key: ${{ runner.os }}-cargo-${{ hashFiles('rustybits//Cargo.lock') }} - shared-key: ${{ runner.os }}-cargo- - workspaces: | - rustybits/ - - - name: make - run: make - - name: selftest - run: | - make selftest - ./zerotier-selftest - - name: 'Tar files' # keeps permissions (execute) - run: tar -cvf zerotier-one.tar zerotier-one - - name: Archive production artifacts - uses: actions/upload-artifact@v4 - with: - name: zerotier-one-ubuntu-x64 - path: zerotier-one.tar - retention-days: 7 - - build_macos: - runs-on: macos-latest - steps: - - name: gitconfig - run: | - git config --global core.autocrlf input - # git config --global core.eol lf - - name: checkout - uses: actions/checkout@v4 - - name: Install Rust aarch64 - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - target: aarch64-apple-darwin - components: rustfmt, clippy - - name: Install Rust x86_64 - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - target: x86_64-apple-darwin - components: rustfmt, clippy - - name: Set up cargo cache - uses: Swatinem/rust-cache@v2 - continue-on-error: false - with: - key: ${{ runner.os }}-cargo-${{ hashFiles('rustybits//Cargo.lock') }} - shared-key: ${{ runner.os }}-cargo- - workspaces: | - rustybits/ - - name: make - run: make - - name: selftest - run: | - make selftest - ./zerotier-selftest - - name: 'Tar files' # keeps permissions (execute) - run: tar -cvf zerotier-one.tar zerotier-one - - name: Archive production artifacts - uses: actions/upload-artifact@v4 - with: - name: zerotier-one-mac - path: zerotier-one.tar - retention-days: 7 - - - build_windows: - runs-on: windows-latest - steps: - - name: gitconfig - run: | - git config --global core.autocrlf true - # git config --global core.eol lf - - name: checkout - uses: actions/checkout@v4 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - target: aarch64-apple-darwin - components: rustfmt, clippy - - name: Set up cargo cache - uses: Swatinem/rust-cache@v2 - continue-on-error: false - with: - key: ${{ runner.os }}-cargo-${{ hashFiles('rustybits//Cargo.lock') }} - shared-key: ${{ runner.os }}-cargo- - workspaces: | - rustybits/ - - - name: setup msbuild - uses: microsoft/setup-msbuild@v2 - - name: msbuild - run: | - msbuild windows\ZeroTierOne.sln /m /p:Configuration=Release /property:Platform=x64 /t:ZeroTierOne - - name: Archive production artifacts - uses: actions/upload-artifact@v4 - with: - name: zerotier-one-windows - path: windows/Build - retention-days: 7 diff --git a/.github/workflows/validate-linux.sh b/.github/workflows/validate-linux.sh deleted file mode 100755 index a661f6f6c..000000000 --- a/.github/workflows/validate-linux.sh +++ /dev/null @@ -1,497 +0,0 @@ -#!/bin/bash - -# This test script joins Earth and pokes some stuff - -TEST_NETWORK=8056c2e21c000001 -RUN_LENGTH=30 -TEST_FINISHED=false -ZTO_VER=$(git describe --tags $(git rev-list --tags --max-count=1)) -ZTO_COMMIT=$(git rev-parse HEAD) -ZTO_COMMIT_SHORT=$(git rev-parse --short HEAD) -TEST_DIR_PREFIX="$ZTO_VER-$ZTO_COMMIT_SHORT-test-results" - -TEST_OK=0 -TEST_FAIL=1 - -echo "Performing test on: $ZTO_VER-$ZTO_COMMIT_SHORT" -TEST_FILEPATH_PREFIX="$TEST_DIR_PREFIX/$ZTO_COMMIT_SHORT" -mkdir $TEST_DIR_PREFIX - -# How long we will wait for ZT to come online before considering it a failure -MAX_WAIT_SECS=30 - -ZT_PORT_NODE_1=9996 -ZT_PORT_NODE_2=9997 - -################################################################################ -# Multi-node connectivity and performance test # -################################################################################ - -test() { - - echo -e "\nPerforming pre-flight checks" - - check_exit_on_invalid_identity - - echo -e "\nRunning test for $RUN_LENGTH seconds" - - export NS1="ip netns exec ns1" - export NS2="ip netns exec ns2" - - export ZT1="$NS1 ./zerotier-cli -p9996 -D$(pwd)/node1" - # Specify custom port on one node to ensure that feature works - export ZT2="$NS2 ./zerotier-cli -p9997 -D$(pwd)/node2" - - echo -e "\nSetting up network namespaces..." - echo "Setting up ns1" - - ip netns add ns1 - $NS1 ip link set dev lo up - ip link add veth0 type veth peer name veth1 - ip link set veth1 netns ns1 - ip addr add 192.168.0.1/24 dev veth0 - ip link set dev veth0 up - - $NS1 ip addr add 192.168.0.2/24 dev veth1 - $NS1 ip link set dev veth1 up - - # Add default route - $NS1 ip route add default via 192.168.0.1 - - iptables -t nat -A POSTROUTING -s 192.168.0.0/255.255.255.0 \ - -o eth0 -j MASQUERADE - iptables -A FORWARD -i eth0 -o veth0 -j ACCEPT - iptables -A FORWARD -o eth0 -i veth0 -j ACCEPT - - echo "Setting up ns2" - ip netns add ns2 - $NS2 ip link set dev lo up - ip link add veth2 type veth peer name veth3 - ip link set veth3 netns ns2 - ip addr add 192.168.1.1/24 dev veth2 - ip link set dev veth2 up - - $NS2 ip addr add 192.168.1.2/24 dev veth3 - $NS2 ip link set dev veth3 up - $NS2 ip route add default via 192.168.1.1 - - iptables -t nat -A POSTROUTING -s 192.168.1.0/255.255.255.0 \ - -o eth0 -j MASQUERADE - iptables -A FORWARD -i eth0 -o veth2 -j ACCEPT - iptables -A FORWARD -o eth0 -i veth2 -j ACCEPT - - # Allow forwarding - sysctl -w net.ipv4.ip_forward=1 - - ################################################################################ - # Memory Leak Check # - ################################################################################ - - export FILENAME_MEMORY_LOG="$TEST_FILEPATH_PREFIX-memory.log" - - echo -e "\nStarting a ZeroTier instance in each namespace..." - - export time_test_start=$(date +%s) - - # Spam the CLI as ZeroTier is starting - spam_cli 100 - - echo "Starting memory leak check" - $NS1 sudo valgrind --demangle=yes --exit-on-first-error=yes \ - --error-exitcode=1 \ - --xml=yes \ - --xml-file=$FILENAME_MEMORY_LOG \ - --leak-check=full \ - ./zerotier-one node1 -p$ZT_PORT_NODE_1 -U >>node_1.log 2>&1 & - - # Second instance, not run in memory profiler - # Don't set up internet access until _after_ zerotier is running - # This has been a source of stuckness in the past. - $NS2 ip addr del 192.168.1.2/24 dev veth3 - $NS2 sudo ./zerotier-one node2 -U -p$ZT_PORT_NODE_2 >>node_2.log 2>&1 & - - sleep 10; # New HTTP control plane is a bit sluggish, so we delay here - - check_bind_to_correct_ports $ZT_PORT_NODE_1 - check_bind_to_correct_ports $ZT_PORT_NODE_2 - - $NS2 ip addr add 192.168.1.2/24 dev veth3 - $NS2 ip route add default via 192.168.1.1 - - echo -e "\nPing from host to namespaces" - - ping -c 3 192.168.0.1 - ping -c 3 192.168.1.1 - - echo -e "\nPing from namespace to host" - - $NS1 ping -c 3 192.168.0.1 - $NS1 ping -c 3 192.168.0.1 - $NS2 ping -c 3 192.168.0.2 - $NS2 ping -c 3 192.168.0.2 - - echo -e "\nPing from ns1 to ns2" - - $NS1 ping -c 3 192.168.0.1 - - echo -e "\nPing from ns2 to ns1" - - $NS2 ping -c 3 192.168.0.1 - - ################################################################################ - # Online Check # - ################################################################################ - - echo "Waiting for ZeroTier to come online before attempting test..." - node1_online=false - node2_online=false - both_instances_online=false - time_zt_node1_start=$(date +%s) - time_zt_node2_start=$(date +%s) - - for ((s = 0; s <= $MAX_WAIT_SECS; s++)); do - node1_online="$($ZT1 -j info | jq '.online' 2>/dev/null)" - node2_online="$($ZT2 -j info | jq '.online' 2>/dev/null)" - echo "Checking for online status: try #$s, node1:$node1_online, node2:$node2_online" - if [[ "$node2_online" == "true" && "$node1_online" == "true" ]]; then - export both_instances_online=true - export time_to_both_nodes_online=$(date +%s) - break - fi - sleep 1 - done - - echo -e "\n\nContents of ZeroTier home paths:" - - ls -lga node1 - tree node1 - ls -lga node2 - tree node2 - - echo -e "\n\nRunning ZeroTier processes:" - echo -e "\nNode 1:\n" - $NS1 ps aux | grep zerotier-one - echo -e "\nNode 2:\n" - $NS2 ps aux | grep zerotier-one - - echo -e "\n\nStatus of each instance:" - - echo -e "\n\nNode 1:\n" - $ZT1 status - echo -e "\n\nNode 2:\n" - $ZT2 status - - if [[ "$both_instances_online" != "true" ]]; then - exit_test_and_generate_report $TEST_FAIL "one or more nodes failed to come online" - fi - - echo -e "\nJoining networks" - - $ZT1 join $TEST_NETWORK - $ZT2 join $TEST_NETWORK - - sleep 10 - - node1_ip4=$($ZT1 get $TEST_NETWORK ip4) - node2_ip4=$($ZT2 get $TEST_NETWORK ip4) - - echo "node1_ip4=$node1_ip4" - echo "node2_ip4=$node2_ip4" - - echo -e "\nPinging each node" - - PING12_FILENAME="$TEST_FILEPATH_PREFIX-ping-1-to-2.txt" - PING21_FILENAME="$TEST_FILEPATH_PREFIX-ping-2-to-1.txt" - - $NS1 ping -c 16 $node2_ip4 >$PING12_FILENAME - $NS2 ping -c 16 $node1_ip4 >$PING21_FILENAME - - ping_loss_percent_1_to_2=$(cat $PING12_FILENAME | - grep "packet loss" | awk '{print $6}' | sed 's/%//') - ping_loss_percent_2_to_1=$(cat $PING21_FILENAME | - grep "packet loss" | awk '{print $6}' | sed 's/%//') - - # Normalize loss value - export ping_loss_percent_1_to_2=$(echo "scale=2; $ping_loss_percent_1_to_2/100.0" | bc) - export ping_loss_percent_2_to_1=$(echo "scale=2; $ping_loss_percent_2_to_1/100.0" | bc) - - ################################################################################ - # CLI Check # - ################################################################################ - - echo "Testing basic CLI functionality..." - - spam_cli 10 - - $ZT1 join $TEST_NETWORK - - $ZT1 -h - $ZT1 -v - $ZT1 status - $ZT1 info - $ZT1 listnetworks - $ZT1 peers - $ZT1 listpeers - - $ZT1 -j status - $ZT1 -j info - $ZT1 -j listnetworks - $ZT1 -j peers - $ZT1 -j listpeers - - $ZT1 dump - - $ZT1 get $TEST_NETWORK allowDNS - $ZT1 get $TEST_NETWORK allowDefault - $ZT1 get $TEST_NETWORK allowGlobal - $ZT1 get $TEST_NETWORK allowManaged - $ZT1 get $TEST_NETWORK bridge - $ZT1 get $TEST_NETWORK broadcastEnabled - $ZT1 get $TEST_NETWORK dhcp - $ZT1 get $TEST_NETWORK id - $ZT1 get $TEST_NETWORK mac - $ZT1 get $TEST_NETWORK mtu - $ZT1 get $TEST_NETWORK name - $ZT1 get $TEST_NETWORK netconfRevision - $ZT1 get $TEST_NETWORK nwid - $ZT1 get $TEST_NETWORK portDeviceName - $ZT1 get $TEST_NETWORK portError - $ZT1 get $TEST_NETWORK status - $ZT1 get $TEST_NETWORK type - - # Test an invalid command - $ZT1 get $TEST_NETWORK derpderp - - # TODO: Validate JSON - - # Performance Test - - export FILENAME_PERF_JSON="$TEST_FILEPATH_PREFIX-iperf.json" - - echo -e "\nBeginning performance test:" - - echo -e "\nStarting server:" - - echo "$NS1 iperf3 -s &" - sleep 1 - - echo -e "\nStarting client:" - sleep 1 - - echo "$NS2 iperf3 --json -c $node1_ip4 > $FILENAME_PERF_JSON" - - cat $FILENAME_PERF_JSON - - # Let ZeroTier idle long enough for various timers - - echo -e "\nIdling ZeroTier for $RUN_LENGTH seconds..." - sleep $RUN_LENGTH - - echo -e "\nLeaving networks" - - $ZT1 leave $TEST_NETWORK - $ZT2 leave $TEST_NETWORK - - sleep 5 - - exit_test_and_generate_report $TEST_OK "completed test" -} - -################################################################################ -# Generate report # -################################################################################ - -exit_test_and_generate_report() { - - echo -e "\nStopping memory check..." - sudo pkill -15 -f valgrind - sleep 10 - - time_test_end=$(date +%s) - - echo "Exiting test with reason: $2 ($1)" - - # Collect ZeroTier dump files - - echo -e "\nCollecting ZeroTier dump files" - - node1_id=$($ZT1 -j status | jq -r .address) - node2_id=$($ZT2 -j status | jq -r .address) - - $ZT1 dump - mv zerotier_dump.txt "$TEST_FILEPATH_PREFIX-node-dump-$node1_id.txt" - - $ZT2 dump - mv zerotier_dump.txt "$TEST_FILEPATH_PREFIX-node-dump-$node2_id.txt" - - # Copy ZeroTier stdout/stderr logs - - cp node_1.log "$TEST_FILEPATH_PREFIX-node-log-$node1_id.txt" - cp node_2.log "$TEST_FILEPATH_PREFIX-node-log-$node2_id.txt" - - # Generate report - - cat $FILENAME_MEMORY_LOG - - DEFINITELY_LOST=$(xmlstarlet sel -t -v '/valgrindoutput/error/xwhat' \ - $FILENAME_MEMORY_LOG | grep "definitely" | awk '{print $1;}') - POSSIBLY_LOST=$(xmlstarlet sel -t -v '/valgrindoutput/error/xwhat' \ - $FILENAME_MEMORY_LOG | grep "possibly" | awk '{print $1;}') - - # Generate coverage report artifact and summary - - FILENAME_COVERAGE_JSON="$TEST_FILEPATH_PREFIX-coverage.json" - FILENAME_COVERAGE_HTML="$TEST_FILEPATH_PREFIX-coverage.html" - - echo -e "\nGenerating coverage test report..." - - gcovr -r . --exclude ext --json-summary $FILENAME_COVERAGE_JSON \ - --html >$FILENAME_COVERAGE_HTML - - cat $FILENAME_COVERAGE_JSON - - COVERAGE_LINE_COVERED=$(cat $FILENAME_COVERAGE_JSON | jq .line_covered) - COVERAGE_LINE_TOTAL=$(cat $FILENAME_COVERAGE_JSON | jq .line_total) - COVERAGE_LINE_PERCENT=$(cat $FILENAME_COVERAGE_JSON | jq .line_percent) - - COVERAGE_LINE_COVERED="${COVERAGE_LINE_COVERED:-0}" - COVERAGE_LINE_TOTAL="${COVERAGE_LINE_TOTAL:-0}" - COVERAGE_LINE_PERCENT="${COVERAGE_LINE_PERCENT:-0}" - - # Default values - - DEFINITELY_LOST="${DEFINITELY_LOST:-0}" - POSSIBLY_LOST="${POSSIBLY_LOST:-0}" - ping_loss_percent_1_to_2="${ping_loss_percent_1_to_2:-100.0}" - ping_loss_percent_2_to_1="${ping_loss_percent_2_to_1:-100.0}" - time_to_both_nodes_online="${time_to_both_nodes_online:--1}" - - # Summarize and emit json for trend reporting - - FILENAME_SUMMARY="$TEST_FILEPATH_PREFIX-summary.json" - - time_length_test=$((time_test_end - time_test_start)) - if [[ $time_to_both_nodes_online != -1 ]]; - then - time_to_both_nodes_online=$((time_to_both_nodes_online - time_test_start)) - fi - #time_length_zt_join=$((time_zt_join_end-time_zt_join_start)) - #time_length_zt_leave=$((time_zt_leave_end-time_zt_leave_start)) - #time_length_zt_can_still_ping=$((time_zt_can_still_ping-time_zt_leave_start)) - - summary=$( - cat <$FILENAME_SUMMARY - cat $FILENAME_SUMMARY - - exit 0 -} - -################################################################################ -# CLI Check # -################################################################################ - -spam_cli() { - echo "Spamming CLI..." - # Rapidly spam the CLI with joins/leaves - - MAX_TRIES="${1:-10}" - - for ((s = 0; s <= MAX_TRIES; s++)); do - $ZT1 status - $ZT2 status - sleep 0.1 - done - - SPAM_TRIES=128 - - for ((s = 0; s <= SPAM_TRIES; s++)); do - $ZT1 join $TEST_NETWORK - done - - for ((s = 0; s <= SPAM_TRIES; s++)); do - $ZT1 leave $TEST_NETWORK - done - - for ((s = 0; s <= SPAM_TRIES; s++)); do - $ZT1 leave $TEST_NETWORK - $ZT1 join $TEST_NETWORK - done -} - -################################################################################ -# Check for proper exit on load of invalid identity # -################################################################################ - -check_exit_on_invalid_identity() { - echo "Checking ZeroTier exits on invalid identity..." - mkdir -p $(pwd)/exit_test - ZT1="sudo ./zerotier-one -p9999 $(pwd)/exit_test" - echo "asdfasdfasdfasdf" > $(pwd)/exit_test/identity.secret - echo "asdfasdfasdfasdf" > $(pwd)/exit_test/authtoken.secret - - echo "Launch ZeroTier with an invalid identity" - $ZT1 & - my_pid=$! - - echo "Waiting 5 seconds" - sleep 5 - - # check if process is running - kill -0 $my_pid - if [ $? -eq 0 ]; then - exit_test_and_generate_report $TEST_FAIL "Exit test FAILED: Process still running after being fed an invalid identity" - fi -} - -################################################################################ -# Check that we're binding to the primary port for TCP/TCP6/UDP # -################################################################################ - -check_bind_to_correct_ports() { - PORT_NUMBER=$1 - echo "Checking bound ports:" - sudo netstat -anp | grep "$PORT_NUMBER" | grep "zerotier" - if [[ $(sudo netstat -anp | grep "$PORT_NUMBER" | grep "zerotier" | grep "tcp") ]]; - then - : - else - exit_test_and_generate_report $TEST_FAIL "ZeroTier did not bind to tcp/$1" - fi - if [[ $(sudo netstat -anp | grep "$PORT_NUMBER" | grep "zerotier" | grep "tcp6") ]]; - then - : - else - exit_test_and_generate_report $TEST_FAIL "ZeroTier did not bind to tcp6/$1" - fi - if [[ $(sudo netstat -anp | grep "$PORT_NUMBER" | grep "zerotier" | grep "udp") ]]; - then - : - else - exit_test_and_generate_report $TEST_FAIL "ZeroTier did not bind to udp/$1" - fi -} - -test "$@" diff --git a/.github/workflows/validate-report.sh b/.github/workflows/validate-report.sh deleted file mode 100755 index 3ae4e1a16..000000000 --- a/.github/workflows/validate-report.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -################################################################################ -# Set exit code depending on tool reports # -################################################################################ - -DEFINITELY_LOST=$(cat *test-results/*summary.json | jq .num_definite_bytes_lost) -EXIT_CODE=$(cat *test-results/*summary.json | jq .exit_code) -EXIT_REASON=$(cat *test-results/*summary.json | jq .exit_reason) - -cat *test-results/*summary.json - -echo -e "\nBytes of memory definitely lost: $DEFINITELY_LOST" - -if [[ "$DEFINITELY_LOST" -gt 0 ]]; then - exit 1 -fi - -# Catch-all for other non-zero exit codes - -if [[ "$EXIT_CODE" -gt 0 ]]; then - echo "Test failed: $EXIT_REASON" - exit 1 -fi \ No newline at end of file diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml deleted file mode 100644 index fbffcadfc..000000000 --- a/.github/workflows/validate.yml +++ /dev/null @@ -1,57 +0,0 @@ -on: - pull_request: - push: - workflow_dispatch: - -jobs: - build_ubuntu: - runs-on: ubuntu-latest - steps: - - name: gitconfig - run: | - git config --global core.autocrlf input - - - name: checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: x86_64-unknown-linux-gnu - override: true - components: rustfmt, clippy - - - name: Set up cargo cache - uses: Swatinem/rust-cache@v2 - continue-on-error: false - with: - key: ${{ runner.os }}-cargo-${{ hashFiles('zeroidc//Cargo.lock') }} - shared-key: ${{ runner.os }}-cargo- - workspaces: | - zeroidc/ - - - name: validate-1m-linux - env: - CC: 'gcc' - CXX: 'g++' - BRANCH: ${{ github.ref_name }} - run: | - sudo apt install -y valgrind xmlstarlet gcovr iperf3 tree - make one ZT_COVERAGE=1 ZT_TRACE=1 - sudo chmod +x ./.github/workflows/validate-linux.sh - sudo ./.github/workflows/validate-linux.sh - - - name: Archive test results - uses: actions/upload-artifact@v4 - with: - name: ${{github.sha}}-test-results - path: "*test-results*" - - - name: final-report - run: | - sudo chmod +x ./.github/workflows/validate-report.sh - sudo ./.github/workflows/validate-report.sh - diff --git a/.gitignore b/.gitignore index ba8b4afca..44b5eb56b 100755 --- a/.gitignore +++ b/.gitignore @@ -6,11 +6,6 @@ /zerotier /nltest -# IDE stuff -/.idea -/.nova -/compile_commands.json - # OS-created garbage files from various platforms .DS_Store .Apple* @@ -35,8 +30,10 @@ Thumbs.db /windows/WebUIWrapper/obj /windows/lib /ext/installfiles/windows/ZeroTier One-SetupFiles +/ext/installfiles/windows/Prerequisites /ext/installfiles/windows/*-cache /ZeroTier One.msi +/windows/.vs *.vcxproj.backup /windows/TapDriver6/Win7Debug /windows/TapDriver6/win7Release @@ -45,7 +42,6 @@ Thumbs.db enc_temp_folder /windows/copyutil/bin /windows/copyutil/obj -.vs/ # *nix/Mac build droppings /build-* @@ -62,6 +58,7 @@ zt1-src.tar.gz *.opensdf *.user *.cache +*.obj *.tlog *.pid *.pkg @@ -106,6 +103,7 @@ windows/ZeroTierOne/Debug/ *.swp *~.nib DerivedData/ +build/ *.pbxuser *.mode1v3 *.mode2v3 @@ -116,27 +114,9 @@ DerivedData/ !default.perspectivev3 *.xccheckout xcuserdata/ +ext/librethinkdbxx/build .vscode __pycache__ *~ attic/world/*.c25519 attic/world/mkworld -workspace/ -workspace2/ -zeroidc/target/ -tcp-proxy/target - -#snapcraft specifics -/parts/ -/stage/ -/prime/ - -*.snap - -.snapcraft -__pycache__ -*.pyc -*_source.tar.bz2 -snap/.snapcraft -tcp-proxy/tcp-proxy -rustybits/target diff --git a/.kick b/.kick deleted file mode 100644 index 28b8ee7f3..000000000 --- a/.kick +++ /dev/null @@ -1,14 +0,0 @@ -kick -kick -kick -kick -kick -kick -kick -kick -kick -kick -kick -kick -kick -kick diff --git a/AUTHORS.md b/AUTHORS.md index 84bb86316..c4c0c730f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,5 +1,3 @@ -# Authors and Third Party Code Licensing Information - ## Primary Authors * ZeroTier Core and ZeroTier One virtual networking service
@@ -28,7 +26,7 @@ ## Third-Party Code -ZeroTier includes the following third party code, either in ext/ or incorporated into the ZeroTier core. This third party code remains licensed under its original license and is not subject to ZeroTier's BSL license. +ZeroTier includes the following third party code, either in ext/ or incorporated into the ZeroTier core. * LZ4 compression algorithm by Yann Collet diff --git a/COPYING b/COPYING index d07f35240..c43e8e7bc 100644 --- a/COPYING +++ b/COPYING @@ -1,8 +1,13 @@ ZeroTier One, an endpoint server for the ZeroTier virtual network layer. Copyright © 2011–2019 ZeroTier, Inc. -ZeroTier is released under the terms of the BSL version 1.1. See the -file LICENSE.txt for details. +ZeroTier One is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or (at +your option) any later version. + +See the file ‘LICENSE.GPL-3’ for the text of the GNU GPL version 3. +If that file is not present, see . .. Local variables: diff --git a/Dockerfile.ci b/Dockerfile.ci deleted file mode 100644 index 9ff3b011a..000000000 --- a/Dockerfile.ci +++ /dev/null @@ -1,28 +0,0 @@ -# vim: ft=dockerfile - -FROM ubuntu:21.04 as stage - -RUN apt-get update -qq && apt-get -qq install make clang -COPY . . -RUN /usr/bin/make -RUN echo $PWD -RUN cp zerotier-one /usr/sbin - -FROM ubuntu:21.04 - -COPY --from=stage /zerotier-one /usr/sbin -RUN ln -sf /usr/sbin/zerotier-one /usr/sbin/zerotier-idtool -RUN ln -sf /usr/sbin/zerotier-one /usr/sbin/zerotier-cli - -RUN echo "${VERSION}" > /etc/zerotier-version -RUN rm -rf /var/lib/zerotier-one - - -RUN apt-get -qq update -RUN apt-get -qq install iproute2 net-tools fping 2ping iputils-ping iputils-arping - -COPY entrypoint.sh.release /entrypoint.sh -RUN chmod 755 /entrypoint.sh - -CMD [] -ENTRYPOINT ["/entrypoint.sh"] diff --git a/Dockerfile.release b/Dockerfile.release deleted file mode 100644 index 2a289cead..000000000 --- a/Dockerfile.release +++ /dev/null @@ -1,23 +0,0 @@ -# vim: ft=dockerfile - -FROM debian:bookworm - -ARG VERSION - -RUN apt-get update -qq && apt-get install curl gpg -y -RUN mkdir -p /usr/share/zerotier && \ - curl -o /usr/share/zerotier/tmp.asc "https://download.zerotier.com/contact%40zerotier.com.gpg" && \ - gpg --no-default-keyring --keyring /usr/share/zerotier/zerotier.gpg --import /usr/share/zerotier/tmp.asc && \ - rm -f /usr/share/zerotier/tmp.asc && \ - echo "deb [signed-by=/usr/share/zerotier/zerotier.gpg] http://download.zerotier.com/debian/bookworm bookworm main" > /etc/apt/sources.list.d/zerotier.list - -RUN apt-get update -qq && apt-get install zerotier-one=${VERSION} curl iproute2 net-tools iputils-ping openssl libssl3 -y -RUN rm -rf /var/lib/zerotier-one - -COPY entrypoint.sh.release /entrypoint.sh -RUN chmod 755 /entrypoint.sh - -HEALTHCHECK --interval=1s CMD bash /healthcheck.sh - -CMD [] -ENTRYPOINT ["/entrypoint.sh"] diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 000000000..88989327e --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,84 @@ +#!/usr/bin/env groovy + +node('master') { + checkout scm + + def changelog = getChangeLog currentBuild + + mattermostSend "Building ${env.JOB_NAME} #${env.BUILD_NUMBER} \n Change Log: \n ${changelog}" +} + +parallel 'centos7': { + node('centos7') { + try { + checkout scm + + stage('Build Centos 7') { + sh 'make -f make-linux.mk' + } + } + catch (err) { + currentBuild.result = "FAILURE" + mattermostSend color: '#ff0000', message: "${env.JOB_NAME} broken on Centos 7 (<${env.BUILD_URL}|Open>)" + + throw err + } + } +// }, 'android-ndk': { +// node('android-ndk') { +// try { +// checkout scm + +// stage('Build Android NDK') { +// sh "/android/android-ndk-r15b/ndk-build -C $WORKSPACE/java ZT1=${WORKSPACE}" +// } +// } +// catch (err) { +// currentBuild.result = "FAILURE" +// mattermostSend color: '#ff0000', message: "${env.JOB_NAME} broken on Android NDK (<${env.BUILD_URL}|Open>)" + +// throw err +// } +// } +// }, 'macOS': { +// node('macOS') { +// try { +// checkout scm + +// stage('Build macOS') { +// sh 'make -f make-mac.mk' +// } + +// stage('Build macOS UI') { +// sh 'cd macui && xcodebuild -target "ZeroTier One" -configuration Debug' +// } +// } +// catch (err) { +// currentBuild.result = "FAILURE" +// mattermostSend color: '#ff0000', message: "${env.JOB_NAME} broken on macOS (<${env.BUILD_URL}|Open>)" + +// throw err +// } +// } +// }, 'windows': { +// node('windows') { +// try { +// checkout scm + +// stage('Build Windows') { +// bat '''CALL "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat" amd64 +// git clean -dfx +// msbuild windows\\ZeroTierOne.sln +// ''' +// } +// } +// catch (err) { +// currentBuild.result = "FAILURE" +// mattermostSend color: '#ff0000', message: "${env.JOB_NAME} broken on Windows (<${env.BUILD_URL}|Open>)" + +// throw err +// } +// } +} + +mattermostSend color: "#00ff00", message: "${env.JOB_NAME} #${env.BUILD_NUMBER} Complete (<${env.BUILD_URL}|Show More...>)" diff --git a/LICENSE.GPL-2 b/LICENSE.GPL-2 new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/LICENSE.GPL-2 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/LICENSE.GPL-3 b/LICENSE.GPL-3 new file mode 100644 index 000000000..94a9ed024 --- /dev/null +++ b/LICENSE.GPL-3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/LICENSE.txt b/LICENSE.txt index 06a3fad64..3807fea36 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,149 +1,37 @@ ------------------------------------------------------------------------------ +ZeroTier One - Network Virtualization Everywhere +Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ -Business Source License 1.1 +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. -License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. -"Business Source License" is a trademark of MariaDB Corporation Ab. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. ------------------------------------------------------------------------------ +You should have received a copy of the GNU General Public License +along with this program. If not, see . -Parameters +See LICENSE.GPL-3 for the full GNU GPL v3 license. -Licensor: ZeroTier, Inc. -Licensed Work: ZeroTier Network Virtualization Engine 1.4.4 - The Licensed Work is (c)2019 ZeroTier, Inc. -Additional Use Grant: You may make use of the Licensed Work, provided you - do not use it in any of the following ways: +-- - * Sell hosted ZeroTier services as a "SaaS" Product +You can be released from the requirements of the license by purchasing +a commercial license. Buying such a license is mandatory as soon as you +develop commercial closed-source software that incorporates or links +directly against ZeroTier software without disclosing the source code +of your own application. - (1) Operate or sell access to ZeroTier root servers, - network controllers, or authorization key or certificate - generation components of the Licensed Work as a - for-profit service, regardless of whether the use of - these components is sold alone or is bundled with other - services. Note that this does not apply to the use of - ZeroTier behind the scenes to operate a service not - related to ZeroTier network administration. +-- - * Create Non-Open-Source Commercial Derivative Works +The above license does not apply to third party code included with or +linked against by ZeroTier software. See the third party code section +of the AUTHORS.md for an index of third party software included in +this software repository. - (2) Link or directly include the Licensed Work in a - commercial or for-profit application or other product - not distributed under an Open Source Initiative (OSI) - compliant license. See: https://opensource.org/licenses - - (3) Remove the name, logo, copyright, or other branding - material from the Licensed Work to create a "rebranded" - or "white labeled" version to distribute as part of - any commercial or for-profit product or service. - - * Certain Government Uses - - (4) Use or deploy the Licensed Work in a government - setting in support of any active government function - or operation with the exception of the following: - physical or mental health care, family and social - services, social welfare, senior care, child care, and - the care of persons with disabilities. - -Change Date: 2026-01-01 - -Change License: Apache License version 2.0 as published by the Apache - Software Foundation - https://www.apache.org/licenses/ - -Alternative Licensing - -If you would like to use the Licensed Work in any way that conflicts with -the stipulations of the Additional Use Grant, contact ZeroTier, Inc. to -obtain an alternative commercial license. - -Visit us on the web at: https://www.zerotier.com/ - -Notice - -The Business Source License (this document, or the "License") is not an Open -Source license. However, the Licensed Work will eventually be made available -under an Open Source License, as stated in this License. - -For more information on the use of the Business Source License for ZeroTier -products, please visit our pricing page which contains license details and -and license FAQ: https://zerotier.com/pricing - -For more information on the use of the Business Source License generally, -please visit the Adopting and Developing Business Source License FAQ at -https://mariadb.com/bsl-faq-adopting. - ------------------------------------------------------------------------------ - -Business Source License 1.1 - -Terms - -The Licensor hereby grants you the right to copy, modify, create derivative -works, redistribute, and make non-production use of the Licensed Work. The -Licensor may make an Additional Use Grant, above, permitting limited -production use. - -Effective on the Change Date, or the fourth anniversary of the first publicly -available distribution of a specific version of the Licensed Work under this -License, whichever comes first, the Licensor hereby grants you rights under -the terms of the Change License, and the rights granted in the paragraph -above terminate. - -If your use of the Licensed Work does not comply with the requirements -currently in effect as described in this License, you must purchase a -commercial license from the Licensor, its affiliated entities, or authorized -resellers, or you must refrain from using the Licensed Work. - -All copies of the original and modified Licensed Work, and derivative works -of the Licensed Work, are subject to this License. This License applies -separately for each version of the Licensed Work and the Change Date may vary -for each version of the Licensed Work released by Licensor. - -You must conspicuously display this License on each original or modified copy -of the Licensed Work. If you receive the Licensed Work in original or -modified form from a third party, the terms and conditions set forth in this -License apply to your use of that work. - -Any use of the Licensed Work in violation of this License will automatically -terminate your rights under this License for the current and all other -versions of the Licensed Work. - -This License does not grant you any right in any trademark or logo of -Licensor or its affiliates (provided that you may use a trademark or logo of -Licensor as expressly required by this License). - -TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON -AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, -EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND -TITLE. - ------------------------------------------------------------------------------ - -MariaDB hereby grants you permission to use this License’s text to license -your works, and to refer to it using the trademark "Business Source License", -as long as you comply with the Covenants of Licensor below. - -Covenants of Licensor - -In consideration of the right to use this License’s text and the "Business -Source License" name and trademark, Licensor covenants to MariaDB, and to all -other recipients of the licensed work to be provided by Licensor: - -1. To specify as the Change License the GPL Version 2.0 or any later version, - or a license that is compatible with GPL Version 2.0 or a later version, - where "compatible" means that software provided under the Change License can - be included in a program with software provided under GPL Version 2.0 or a - later version. Licensor may specify additional Change Licenses without - limitation. - -2. To either: (a) specify an additional grant of rights to use that does not - impose any additional restriction on the right granted in this License, as - the Additional Use Grant; or (b) insert the text "None". - -3. To specify a Change Date. - -4. Not to modify this License in any other way. +Licenses for third party code are all relatively permissive: MIT, +BSD, and public domain. The only exception is the tap-windows driver +which is under the GPLv2, but this is only needed to produce the +binary tap device driver used by the ZeroTier service on Windows. diff --git a/Makefile b/Makefile index f77767fb8..144225fc4 100644 --- a/Makefile +++ b/Makefile @@ -17,8 +17,8 @@ ifeq ($(OSTYPE),FreeBSD) include make-bsd.mk endif ifeq ($(OSTYPE),OpenBSD) - CC=clang - CXX=clang++ + CC=egcc + CXX=eg++ ZT_BUILD_PLATFORM=9 include make-bsd.mk endif @@ -26,11 +26,3 @@ endif ifeq ($(OSTYPE),NetBSD) include make-netbsd.mk endif - -drone: - @echo "rendering .drone.yaml from .drone.jsonnet" - drone jsonnet --format --stream - drone sign zerotier/ZeroTierOne --save - -clang-format: - find node osdep service tcp-proxy controller -iname '*.cpp' -o -iname '*.hpp' | xargs clang-format -i diff --git a/OFFICIAL-RELEASE-STEPS.md b/OFFICIAL-RELEASE-STEPS.md index f7842e9fc..6de3526cf 100644 --- a/OFFICIAL-RELEASE-STEPS.md +++ b/OFFICIAL-RELEASE-STEPS.md @@ -14,7 +14,7 @@ The version must be incremented in all of the following files: /debian/changelog /ext/installfiles/mac/ZeroTier One.pkgproj /ext/installfiles/windows/ZeroTier One.aip - ../DesktopUI/mac-app-template/ZeroTier.app/Contents/Info.plist + /windows/WinUI/AboutView.xaml The final .AIP file can only be edited on Windows with [Advanced Installer Enterprise](http://www.advancedinstaller.com/). In addition to incrementing the version be sure that a new product code is generated. (The "upgrade code" GUID on the other hand must never change.) diff --git a/README.docker.md b/README.docker.md deleted file mode 100644 index 5b046e320..000000000 --- a/README.docker.md +++ /dev/null @@ -1,73 +0,0 @@ -# ZeroTier One in a container! - -**NOTE:** _Most of this information pertains to the docker image only. For more information about ZeroTier, check out the repository_: [here](https://github.com/zerotier/ZeroTierOne) or the [commercial website](https://www.zerotier.com). - -[ZeroTier](https://www.zerotier.com) is a smart programmable Ethernet switch for planet Earth. It allows all networked devices, VMs, containers, and applications to communicate as if they all reside in the same physical data center or cloud region. - -This is accomplished by combining a cryptographically addressed and secure peer to peer network (termed VL1) with an Ethernet emulation layer somewhat similar to VXLAN (termed VL2). Our VL2 Ethernet virtualization layer includes advanced enterprise SDN features like fine grained access control rules for network micro-segmentation and security monitoring. - -All ZeroTier traffic is encrypted end-to-end using secret keys that only you control. Most traffic flows peer to peer, though we offer free (but slow) relaying for users who cannot establish peer to peer connections. - -The goals and design principles of ZeroTier are inspired by among other things the original [Google BeyondCorp](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/43231.pdf) paper and the [Jericho Forum](https://en.wikipedia.org/wiki/Jericho_Forum) with its notion of "deperimeterization." - -Visit [ZeroTier's site](https://www.zerotier.com/) for more information and [pre-built binary packages](https://www.zerotier.com/download/). Apps for Android and iOS are available for free in the Google Play and Apple app stores. - -ZeroTier is licensed under the [BSL version 1.1](https://mariadb.com/bsl11/). See [LICENSE.txt](https://github.com/zerotier/ZeroTierOne/blob/master/LICENSE.txt) and the [ZeroTier pricing page](https://www.zerotier.com/pricing) for details. ZeroTier is free to use internally in businesses and academic institutions and for non-commercial purposes. Certain types of commercial use such as building closed-source apps and devices based on ZeroTier or offering ZeroTier network controllers and network management as a SaaS service require a commercial license. - -A small amount of third party code is also included in ZeroTier and is not subject to our BSL license. See [AUTHORS.md](https://github.com/zerotier/ZeroTierOne/blob/master/AUTHORS.md) for a list of third party code, where it is included, and the licenses that apply to it. All of the third party code in ZeroTier is liberally licensed (MIT, BSD, Apache, public domain, etc.). - -## Building the docker image - -Due to the network being a substrate for most applications and not an application unto itself, it makes sense that many people would want to build their own image based on our formula. - -The image is based on `debian:buster`. - -The `Dockerfile.release` file contains build instructions for building the described image in the rest of the README. The build is multi-arch and multi-release capable. - -These build arguments power the build: - -- `PACKAGE_BASEURL`: The base URL of the package repository to fetch from. (default: `https://download.zerotier.com/debian/buster/pool/main/z/zerotier-one/`) -- `ARCH`: The architecture of the package, in debian format. Must match your image arch. (default: `amd64`) -- `VERSION`: **REQUIRED** the version of ZeroTier to fetch. - -You can build this image like so: - -``` -docker build -f Dockerfile.release -t mybuild --build-arg VERSION=1.6.5 . -``` - -## Using the docker image - -The `entrypoint.sh` in the docker image is a little different; zerotier will be spawned in the background and the "main process" is actually just a sleeping shell script. This allows `zerotier-one` to gracefully terminate in some situations largely unique to docker. - -The `zerotier/zerotier` image requires the `CAP_NET_ADMIN` capability and the `/dev/net/tun` device must be forwarded to it. - -To join a network, simply supply it on the command-line; you can supply multiple networks. - -``` -docker run --name myzerotier --rm --cap-add NET_ADMIN --device /dev/net/tun zerotier/zerotier:latest abcdefdeadbeef00 -``` - -Once joining all the networks you have provided, it will sleep until terminated. Note that in ZeroTier, joining a network does not necessarily mean you have an IP or can do anything, really. You will want to probe the control socket: - -``` -docker exec myzerotier zerotier-cli listnetworks -``` - -To ensure you have a network available before trying to listen on it. Without pre-configuring the identity, this usually means going to the central admin panel and clicking the checkmark against your zerotier identity. - -### Environment Variables - -You can control a few settings including the identity used and the authtoken used to interact with the control socket (which you can forward and access through `localhost:9993`). - -- `ZEROTIER_JOIN_NETWORKS`: additional way to set networks to join. -- `ZEROTIER_API_SECRET`: replaces the `authtoken.secret` before booting and allows you to manage the control socket's authentication key. -- `ZEROTIER_IDENTITY_PUBLIC`: the `identity.public` file for zerotier-one. Use `zerotier-idtool` to generate one of these for you. -- `ZEROTIER_IDENTITY_SECRET`: the `identity.secret` file for zerotier-one. Use `zerotier-idtool` to generate one of these for you. -- `ZEROTIER_LOCAL_CONF`: Sets the the `local.conf` file content for zerotier-one - -### Tips - -- Forwarding port `:9993` to somewhere outside is probably a good idea for highly trafficked services. -- Forwarding `localhost:9993` to a control network where you can drive it remotely might be a good idea, just be sure to set your authtoken properly through environment variables. -- Pre-generating your identities could be much simpler to do via our [terraform plugin](https://github.com/zerotier/terraform-provider-zerotier) diff --git a/README.md b/README.md index e881ce810..6fecf2fdc 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,19 @@ ZeroTier - Global Area Networking ====== -*This document is written for a software developer audience. For information on using ZeroTier, see the: [Website](https://www.zerotier.com), [Documentation Site](https://docs.zerotier.com), and [Discussion Forum](https://discuss.zerotier.com).* +ZeroTier is a smart programmable Ethernet switch for planet Earth. It allows networked devices and applications to be managed as if the entire world is one data center or cloud region. -ZeroTier is a smart programmable Ethernet switch for planet Earth. It allows all networked devices, VMs, containers, and applications to communicate as if they all reside in the same physical data center or cloud region. - -This is accomplished by combining a cryptographically addressed and secure peer to peer network (termed VL1) with an Ethernet emulation layer somewhat similar to VXLAN (termed VL2). Our VL2 Ethernet virtualization layer includes advanced enterprise SDN features like fine grained access control rules for network micro-segmentation and security monitoring. - -All ZeroTier traffic is encrypted end-to-end using secret keys that only you control. Most traffic flows peer to peer, though we offer free (but slow) relaying for users who cannot establish peer to peer connections. - -The goals and design principles of ZeroTier are inspired by among other things the original [Google BeyondCorp](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/43231.pdf) paper and the [Jericho Forum](https://en.wikipedia.org/wiki/Jericho_Forum) with its notion of "deperimeterization." +It replaces the physical LAN/WAN boundary with a virtual one, allowing devices of any type at any location to be managed as if they all reside in the same cloud region or data center. All traffic is encrypted end-to-end and takes the most direct path available for minimum latency and maximum performance. The goals and design of ZeroTier are inspired by among other things the original [Google BeyondCorp](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/43231.pdf) paper and the [Jericho Forum](https://en.wikipedia.org/wiki/Jericho_Forum). Visit [ZeroTier's site](https://www.zerotier.com/) for more information and [pre-built binary packages](https://www.zerotier.com/download/). Apps for Android and iOS are available for free in the Google Play and Apple app stores. -ZeroTier is licensed under the [BSL version 1.1](https://mariadb.com/bsl11/). See [LICENSE.txt](LICENSE.txt) and the [ZeroTier pricing page](https://www.zerotier.com/pricing) for details. ZeroTier is free to use internally in businesses and academic institutions and for non-commercial purposes. Certain types of commercial use such as building closed-source apps and devices based on ZeroTier or offering ZeroTier network controllers and network management as a SaaS service require a commercial license. - -A small amount of third party code is also included in ZeroTier and is not subject to our BSL license. See [AUTHORS.md](AUTHORS.md) for a list of third party code, where it is included, and the licenses that apply to it. All of the third party code in ZeroTier is liberally licensed (MIT, BSD, Apache, public domain, etc.). - ### Getting Started -Everything in the ZeroTier world is controlled by two types of identifier: 40-bit/10-digit *ZeroTier addresses* and 64-bit/16-digit *network IDs*. These identifiers are easily distinguished by their length. A ZeroTier address identifies a node or "device" (laptop, phone, server, VM, app, etc.) while a network ID identifies a virtual Ethernet network that can be joined by devices. +Everything in the ZeroTier world is controlled by two types of identifier: 40-bit/10-digit *ZeroTier addresses* and 64-bit/16-digit *network IDs*. A ZeroTier address identifies a node or "device" (laptop, phone, server, VM, app, etc.) while a network ID identifies a virtual Ethernet network that can be joined by devices. -ZeroTier addresses can be thought of as port numbers on an enormous planet-wide enterprise Ethernet smart switch supporting VLANs. Network IDs are VLAN IDs to which these ports may be assigned. A single port can be assigned to more than one VLAN. +Another way of thinking about it is that ZeroTier addresses are port numbers on a giant planetary-sized smart switch while network IDs are VLANs to which these ports can be assigned. For more details read about VL1 and VL2 in [the ZeroTier manual](https://www.zerotier.com/manual/). -A ZeroTier address looks like `8056c2e21c` and a network ID looks like `8056c2e21c000001`. Network IDs are composed of the ZeroTier address of that network's primary controller and an arbitrary 24-bit ID that identifies the network on this controller. Network controllers are roughly analogous to SDN controllers in SDN protocols like [OpenFlow](https://en.wikipedia.org/wiki/OpenFlow), though as with the analogy between VXLAN and VL2 this should not be read to imply that the protocols or design are the same. You can use our convenient and inexpensive SaaS hosted controllers at [my.zerotier.com](https://my.zerotier.com/) or [run your own controller](controller/) if you don't mind messing around with JSON configuration files or writing scripts to do so. +*Network controllers* are ZeroTier nodes that act as access control certificate authorities and configuration managers for virtual networks. The first 40 bits (or 10 digits) of a network ID is the ZeroTier address of its controller. You can create networks with our [hosted controllers](https://my.zerotier.com/) and web UI/API or [host your own](controller/) if you don't mind posting some JSON configuration info or writing a script to do so. ### Project Layout @@ -34,164 +24,86 @@ The base path contains the ZeroTier One service main entry point (`one.cpp`), se - `controller/`: the reference network controller implementation, which is built and included by default on desktop and server build targets. - `debian/`: files for building Debian packages on Linux. - `doc/`: manual pages and other documentation. + - `docker/`: Dockerfile to build as a container for containerized Linux systems and Kubernetes clusters. - `ext/`: third party libraries, binaries that we ship for convenience on some platforms (Mac and Windows), and installation support files. - `include/`: include files for the ZeroTier core. - `java/`: a JNI wrapper used with our Android mobile app. (The whole Android app is not open source but may be made so in the future.) + - `macui/`: a Macintosh menu-bar app for controlling ZeroTier One, written in Objective C. - `node/`: the ZeroTier virtual Ethernet switch core, which is designed to be entirely separate from the rest of the code and able to be built as a stand-alone OS-independent library. Note to developers: do not use C++11 features in here, since we want this to build on old embedded platforms that lack C++11 support. C++11 can be used elsewhere. - `osdep/`: code to support and integrate with OSes, including platform-specific stuff only built for certain targets. - `rule-compiler/`: JavaScript rules language compiler for defining network-level rules. - `service/`: the ZeroTier One service, which wraps the ZeroTier core and provides VPN-like connectivity to virtual networks for desktops, laptops, servers, VMs, and containers. - - `windows/`: Visual Studio solution files, Windows service code, and the Windows task bar app UI. - - `zeroidc/`: OIDC implementation used by ZeroTier service to log into SSO-enabled networks. (This part is written in Rust, and more Rust will be appearing in this repository in the future.) - -### Contributing - -Please do pull requests off of the `dev` branch. - -Releases are done by merging `dev` into `main` and then tagging and doing builds. + - `windows/`: Visual Studio solution files, Windows service code for ZeroTier One, and the Windows task bar app UI. ### Build and Platform Notes -To build on Mac and Linux just type `make`. On FreeBSD and OpenBSD `gmake` (GNU make) is required and can be installed from packages or ports. For Windows there is a Visual Studio solution in `windows/`. +To build on Mac and Linux just type `make`. On FreeBSD and OpenBSD `gmake` (GNU make) is required and can be installed from packages or ports. For Windows there is a Visual Studio solution in `windows/'. - **Mac** - - Xcode command line tools for macOS 10.13 or newer are required. - - Rust for x86_64 and ARM64 targets *if SSO is enabled in the build*. + - Xcode command line tools for OSX 10.8 or newer are required. - **Linux** - - The minimum compiler versions required are GCC/G++ 8.x or CLANG/CLANG++ 5.x. + - The minimum compiler versions required are GCC/G++ 4.9.3 or CLANG/CLANG++ 3.4.2. (Install `clang` on CentOS 7 as G++ is too old.) - Linux makefiles automatically detect and prefer clang/clang++ if present as it produces smaller and slightly faster binaries in most cases. You can override by supplying CC and CXX variables on the make command line. - - Rust for x86_64 and ARM64 targets *if SSO is enabled in the build*. - **Windows** - - Visual Studio 2022 on Windows 10 or newer. - - Rust for x86_64 and ARM64 targets *if SSO is enabled in the build*. + - Windows 7 or newer is supported. This *may* work on Vista but isn't officially supported there. It will not work on Windows XP. + - We build with Visual Studio 2017. Older versions may not work. Clang or MinGW will also probably work but may require some makefile hacking. - **FreeBSD** - GNU make is required. Type `gmake` to build. - - `binutils` is required. Type `pkg install binutils` to install. - - Rust for x86_64 and ARM64 targets *if SSO is enabled in the build*. - **OpenBSD** - There is a limit of four network memberships on OpenBSD as there are only four tap devices (`/dev/tap0` through `/dev/tap3`). - GNU make is required. Type `gmake` to build. - - Rust for x86_64 and ARM64 targets *if SSO is enabled in the build*. Typing `make selftest` will build a *zerotier-selftest* binary which unit tests various internals and reports on a few aspects of the build environment. It's a good idea to try this on novel platforms or architectures. ### Running -Running *zerotier-one* with `-h` option will show help. +Running *zerotier-one* with -h will show help. -On Linux and BSD, if you built from source, you can start the service with: +On Linux and BSD you can start the service with: sudo ./zerotier-one -d -On most distributions, macOS, and Windows, the installer will start the service and set it up to start on boot. - A home folder for your system will automatically be created. -The service is controlled via the JSON API, which by default is available at `127.0.0.1:9993`. It also listens on `0.0.0.0:9993` which is only usable if `allowManagementFrom` is properly configured in `local.conf`. We include a *zerotier-cli* command line utility to make API calls for standard things like joining and leaving networks. The *authtoken.secret* file in the home folder contains the secret token for accessing this API. See [service/README.md](service/README.md) for API documentation. +The service is controlled via the JSON API, which by default is available at 127.0.0.1 port 9993. We include a *zerotier-cli* command line utility to make API calls for standard things like joining and leaving networks. The *authtoken.secret* file in the home folder contains the secret token for accessing this API. See README.md in [service/](service/) for API documentation. Here's where home folders live (by default) on each OS: * **Linux**: `/var/lib/zerotier-one` * **FreeBSD** / **OpenBSD**: `/var/db/zerotier-one` * **Mac**: `/Library/Application Support/ZeroTier/One` - * **Windows**: `\ProgramData\ZeroTier\One` (That's the default. The base 'shared app data' folder might be different if Windows is installed with a non-standard drive letter assignment or layout.) + * **Windows**: `\ProgramData\ZeroTier\One` (That's for Windows 7. The base 'shared app data' folder might be different on different Windows versions.) -### Basic Troubleshooting +Running ZeroTier One on a Mac is the same, but OSX requires a kernel extension. We ship a signed binary build of the ZeroTier tap device driver, which can be installed on Mac with: + + sudo make install-mac-tap + +This will create the home folder for Mac, place *tap.kext* there, and set its modes correctly to enable ZeroTier One to manage it with *kextload* and *kextunload*. + +### Troubleshooting For most users, it just works. -If you are running a local system firewall, we recommend adding a rules permitting zerotier. If you installed binaries for Windows this should be done automatically. Other platforms might require manual editing of local firewall rules depending on your configuration. +If you are running a local system firewall, we recommend adding a rule permitting UDP port 9993 inbound and outbound. If you installed binaries for Windows this should be done automatically. Other platforms might require manual editing of local firewall rules depending on your configuration. -See the [documentation site](https://docs.zerotier.com/zerotier/troubleshooting) for more information. +The Mac firewall can be found under "Security" in System Preferences. Linux has a variety of firewall configuration systems and tools. If you're using Ubuntu's *ufw*, you can do this: -The Mac firewall can be found under "Security" in System Preferences. Linux has a variety of firewall configuration systems and tools. + sudo ufw allow 9993/udp On CentOS check `/etc/sysconfig/iptables` for IPTables rules. For other distributions consult your distribution's documentation. You'll also have to check the UIs or documentation for commercial third party firewall applications like Little Snitch (Mac), McAfee Firewall Enterprise (Windows), etc. if you are running any of those. Some corporate environments might have centrally managed firewall software, so you might also have to contact IT. ZeroTier One peers will automatically locate each other and communicate directly over a local wired LAN *if UDP port 9993 inbound is open*. If that port is filtered, they won't be able to see each others' LAN announcement packets. If you're experiencing poor performance between devices on the same physical network, check their firewall settings. Without LAN auto-location peers must attempt "loopback" NAT traversal, which sometimes fails and in any case requires that every packet traverse your external router twice. -Users behind certain types of firewalls and "symmetric" NAT devices may not be able to connect to external peers directly at all. ZeroTier has limited support for port prediction and will *attempt* to traverse symmetric NATs, but this doesn't always work. If P2P connectivity fails you'll be bouncing UDP packets off our relay servers resulting in slower performance. Some NAT router(s) have a configurable NAT mode, and setting this to "full cone" will eliminate this problem. If you do this you may also see a magical improvement for things like VoIP phones, Skype, BitTorrent, WebRTC, certain games, etc., since all of these use NAT traversal techniques similar to ours. +Users behind certain types of firewalls and "symmetric" NAT devices may not able able to connect to external peers directly at all. ZeroTier has limited support for port prediction and will *attempt* to traverse symmetric NATs, but this doesn't always work. If P2P connectivity fails you'll be bouncing UDP packets off our relay servers resulting in slower performance. Some NAT router(s) have a configurable NAT mode, and setting this to "full cone" will eliminate this problem. If you do this you may also see a magical improvement for things like VoIP phones, Skype, BitTorrent, WebRTC, certain games, etc., since all of these use NAT traversal techniques similar to ours. + +If you're interested, there's a [technical deep dive about NAT traversal on our blog](https://www.zerotier.com/blog/?p=226?pk_campaign=github_ZeroTierOne). A troubleshooting tool to help you diagnose NAT issues is planned for the future as are uPnP/IGD/NAT-PMP and IPv6 transport. If a firewall between you and the Internet blocks ZeroTier's UDP traffic, you will fall back to last-resort TCP tunneling to rootservers over port 443 (https impersonation). This will work almost anywhere but is *very slow* compared to UDP or direct peer to peer connectivity. -Additional help can be found in our [knowledge base](https://zerotier.atlassian.net/wiki/spaces/SD/overview). +### Contributing -### Prometheus Metrics +Please make pull requests against the `dev` branch. The `master` branch is release, and `edge` is for unstable and work in progress changes and is not likely to work. -Prometheus Metrics are available at the `/metrics` API endpoint. This endpoint is protected by an API key stored in `metricstoken.secret` to prevent unwanted information leakage. Information that could be gleaned from the metrics include joined networks and peers your instance is talking to. +### License -Access control is via the ZeroTier control interface itself and `metricstoken.secret`. This can be sent as a bearer auth token, via the `X-ZT1-Auth` HTTP header field, or appended to the URL as `?auth=`. You can see the current metrics via `cURL` with the following command: - - // Linux - curl -H "X-ZT1-Auth: $(sudo cat /var/lib/zerotier-one/metricstoken.secret)" http://localhost:9993/metrics - - // macOS - curl -H "X-ZT1-Auth: $(sudo cat /Library/Application\ Support/ZeroTier/One/metricstoken.secret)" http://localhost:9993/metrics - - // Windows PowerShell (Admin) - Invoke-RestMethod -Headers @{'X-ZT1-Auth' = "$(Get-Content C:\ProgramData\ZeroTier\One\metricstoken.secret)"; } -Uri http://localhost:9993/metrics - -To configure a scrape job in Prometheus on the machine ZeroTier is running on, add this to your Prometheus `scrape_config`: - - - job_name: zerotier-one - honor_labels: true - scrape_interval: 15s - metrics_path: /metrics - static_configs: - - targets: - - 127.0.0.1:9993 - labels: - group: zerotier-one - node_id: $YOUR_10_CHARACTER_NODE_ID - authorization: - credentials: $YOUR_METRICS_TOKEN_SECRET - -If neither of these methods are desirable, it is probably possible to distribute metrics via [Prometheus Proxy](https://github.com/pambrose/prometheus-proxy) or some other tool. Note: We have not tested this internally, but will probably work with the correct configuration. - -Metrics are also available on disk in ZeroTier's working directory: - - // Linux - /var/lib/zerotier-one/metrics.prom - - // macOS - /Library/Application\ Support/ZeroTier/One/metrics.prom - - //Windows - C:\ProgramData\ZeroTier\One\metrics.prom - -#### Available Metrics - -| Metric Name | Labels | Metric Type | Description | -| --- | --- | --- | --- | -| zt_packet | packet_type, direction | Counter | ZeroTier packet type counts | -| zt_packet_error | error_type, direction | Counter | ZeroTier packet errors| -| zt_data | protocol, direction | Counter | number of bytes ZeroTier has transmitted or received | -| zt_num_networks | | Gauge | number of networks this instance is joined to | -| zt_network_multicast_groups_subscribed | network_id | Gauge | number of multicast groups networks are subscribed to | -| zt_network_packets | network_id, direction | Counter | number of incoming/outgoing packets per network | -| zt_peer_latency | node_id | Histogram | peer latency (ms) | -| zt_peer_path_count | node_id, status | Gauge | number of paths to peer | -| zt_peer_packets | node_id, direction | Counter | number of packets to/from a peer | -| zt_peer_packet_errors | node_id | Counter | number of incoming packet errors from a peer | - -If there are other metrics you'd like to see tracked, ask us in an Issue or send us a Pull Request! - -### HTTP / App server - -There is a static http file server suitable for hosting Single Page Apps at http://localhost:9993/app/ - -Use `zerotier-cli info -j` to find your zerotier-one service's homeDir - -``` sh -cd $ZT_HOME -sudo mkdir -p app/app1 -sudo mkdir -p app/appB -echo 'appA

hello world A' | sudo tee app/appA/index.html -echo 'app2

hello world 2' | sudo tee app/app2/index.html -curl -sL http://localhost:9993/app/appA http://localhost:9993/app/app2 -``` - -Then visit [http://localhost:9993/app/app1/](http://localhost:9993/app/app1/) and [http://localhost:9993/app/appB/](http://localhost:9993/app/appB/) - -Requests to paths don't exist return the app root index.html, as is customary for SPAs. -If you want, you can write some javascript that talks to the service or controller [api](https://docs.zerotier.com/service/v1). +The ZeroTier source code is open source and is licensed under the GNU GPL v3 (not LGPL). If you'd like to embed it in a closed-source commercial product or appliance, please e-mail [contact@zerotier.com](mailto:contact@zerotier.com) to discuss commercial licensing. Otherwise it can be used for free. diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index e6582207e..1dd56b9fd 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,300 +1,6 @@ ZeroTier Release Notes ====== -# 2024-10-23 -- Version 1.14.2 - - * Fix for missing entitlement on macOS Sequoia. - * Fix for a problem correctly parsing local.conf to enable low bandwidth mode. - * Increment versions of some dependent libraries. - * Other fixes. - -# 2024-09-12 -- Version 1.14.1 - - * Multithreaded packet I/O support! Currently this is just for Linux and must - be enabled in local.conf. It will likely make the largest difference on small - multi-core devices where CPU is a bottleneck and high throughput is desired. - It may be enabled by default in the future but we want it to be thoroughly - tested. It's a little harder than it seems at first glance due to the need - to keep packets in sequence and balance load. - * Several multipath bug fixes. - * Updated the versions on a number of libraries related to OIDC support and HTTP. - * MacOS .app now shows the correct version in its Info.plist manifest. - * Sanitize MAC addresses in JSON format rules parser. - * Some basic information about the platform (OS, CPU architecture) is now reported - to network controllers when networks are joined so it can be displayed to - network admins and in the future used in policy checking and inventory operations. - -# 2024-05-02 -- Version 1.14.0 - - * Linux I/O performance improvements under heavy load - * Improvements to multipath - * Fix for port rebinding "coma" bug after periods offline (some laptop users) - * Fixed a rules engine quirk/ambiguity (GitHub Issue #2200) - * Controller API enhancements: node names and other node meta-data - * Other bug fixes - -# 2023-09-12 -- Version 1.12.2 - - * More improvements to macOS full tunnel mode. - * Faster recovery after changes to physical network settings. - -# 2023-08-25 -- Version 1.12.1 - - * Minor release to fix a port binding issue in Linux. - * Update Debian dependencies. - * No changes for other platforms. - -# 2023-08-23 -- Version 1.12.0 - - * Experimental Windows ARM64 support - * Fix numerous sleep/wake issues on macOS and other platforms - * Faster recovery after changes to physical network settings - * Prometheus compatible metrics support! - * Fix full tunnel mode on recent macOS versions - * Numerous macOS DNS fixes - * 10-30% speed improvement on Linux - -# 2023-03-23 -- Version 1.10.6 - - * Prevent binding temporary ipv6 addresses on macos (#1910) - * Prevent path-learning loops (#1914) - * Prevent infinite loop of UAC prompts in tray app - -# 2023-03-10 -- Version 1.10.5 - - * Fix for high CPU usage bug on Windows - -# 2023-03-07 -- Version 1.10.4 - - * SECURITY FIX (Windows): this version fixes a file permission problem on - Windows that could allow non-privileged users on a Windows system to read - privileged files in the ZeroTier service's working directory. This could - allow an unprivileged local Windows user to administrate the local ZeroTier - instance without appropriate local permissions. This issue is not remotely - exploitable unless a remote user can read arbitrary local files, and does - not impact other operating systems. - - * Fix a bug in the handling of multiple IP address assignments to virtual - interfaces on macOS. - -# 2023-02-15 -- Version 1.10.3 - - * Fix for duplicate paths in client. Could cause connectivity issues. Affects all platforms. - * Fix for Ethernet Tap MTU setting, would not properly apply on Linux. - * Fix default route bugs (macOS.) - * Enable Ping automatically for ZeroTier Adapters (Windows.) - * SSO updates and minor bugfixes. - * Add low-bandwidth mode. - * Add forceTcpRelay mode (optionally enabled.) - * Fix bug that prevented setting of custom TCP relay address. - * Build script improvements and bug fixes. - -# 2022-11-01 -- Version 1.10.2 - - * Fix another SSO "stuck client" issue in zeroidc. - * Expose root-reported external IP/port information via the local JSON API for better diagnostics. - * Multipath: CLI output improvement for inspecting bonds - * Multipath: balance-aware mode - * Multipath: Custom policies - * Multipath: Link quality measurement improvements - -Note that releases are coming few and far between because most of our dev effort is going into version 2. - -# 2022-06-27 -- Version 1.10.1 - - * Fix an issue that could cause SSO clients to get "stuck" on stale auth URLs. - * A few other SSO related bug fixes. - -# 2022-06-07 -- Version 1.10.0 - - * Fix formatting problem in `zerotier-cli` when using SSO networks. - * Fix a few other minor bugs in SSO signin to prepare for general availability. - * Remove requirement for webview in desktop UI and instead just make everything available via the tray pulldown/menu. Use [libui-ng](https://github.com/libui-ng/libui-ng) for minor prompt dialogs. Saves space and eliminates installation headaches on Windows. - * Fix SSO "spam" bug in desktop UI. - * Use system default browser for SSO login so all your plugins, MFA devices, password managers, etc. will work as you have them configured. - * Minor fix for bonding/multipath. - -# 2022-05-10 -- Version 1.8.10 - - * Fixed a bug preventing SSO sign-on on Windows. - -# 2022-04-25 -- Version 1.8.9 - - * Fixed a long-standing and strange bug that was causing sporadic "phantom" packet authentication failures. Not a security problem but could be behind sporadic reports of link failures under some conditions. - * Fixed a memory leak in SSO/OIDC support. - * Fixed SSO/OIDC display error on CLI. - * Fixed a bug causing nodes to sometimes fail to push certs to each other (primarily affects SSO/OIDC use cases). - * Fixed a deadlock bug on leaving SSO/OIDC managed networks. - * Added some new Linux distributions to the build subsystem. - -# 2022-04-11 -- Version 1.8.8 - - * Fix a local privilege escalation bug in the Windows installer. - * Dependency fix for some Ubuntu versions. - * No changes for other platforms. Windows upgrade recommended, everyone else optional. - -# 2022-03-30 -- Version 1.8.7 - - * Fix for dependency installations in Windows MSI package. - * Fix for desktop UI setup when run by a non-super-user. - * Bug fix in local OIDC / SSO support for auth0 and other providers. - * Other minor fixes for e.g. old Linux distributions. - -# 2022-03-04 -- Version 1.8.6 - - * Fixed an issue that could cause the UI to be non-responsive if not joined to any networks. - * Fix dependency issues in Debian and RedHat packages for some distributions (Fedora, Mint). - * Bumped the peer cache serialization version to prevent "coma" issues on upgrade due to changes in path logic behaving badly with old values. - -# 2022-02-22 -- Version 1.8.5 - - * Plumbing under the hood for endpoint device SSO support. - * Fix in LinuxEthernetTap to tap device support on very old (2.6) Linux kernels. - * Fix an issue that could cause self-hosted roots ("moons") to fail to assist peers in making direct links. (GitHub issue #1512) - * Merge a series of changes by Joseph Henry (of ZeroTier) that should fix some edge cases where ZeroTier would "forget" valid paths. - * Minor multipath improvements for automatic path negotiation. - -# 2021-11-30 -- Version 1.8.4 - - * Fixed an ugly font problem on some older macOS versions. - * Fixed a bug that could cause the desktop tray app control panel to stop opening after a while on Windows. - * Fixed a possible double "release" in macOS tray app code that crashed on older macOS versions. - * Fixed installation on 32-bit Windows 10. - * Fixed a build flags issue that could cause ZeroTier to crash on older ARM32 CPUs. - -# 2021-11-15 -- Version 1.8.3 - - * Remove problematic spinlock, which was only used on x86_64 anyway. Just use pthread always. - * Fix fd leak on MacOS that caused non-responsiveness after some time. - * Fix Debian install scripts to set /usr/sbin/nologin as shell on service user. - * Fix regression that could prevent managed routes from being deleted. - * DesktopUI: Remove NSDate:now() call, now works on MacOS 10.13 or newer! - -# 2021-11-08 -- Version 1.8.2 - - * Fix multicast on linux. - * Fix a bug that could cause the tap adapter to have the wrong MAC on Linux. - * Update build flags to possibly support MacOS older than 10.14, but more work needs to be done. It may not work yet. - * Fix path variable setting on Windows. - -# 2021-10-28 -- Version 1.8.1 - - * Fix numerous UI issues from 1.8.0 (never fully released). - * Remove support for REALLY ancient 1.1.6 or earlier network controllers. - * MacOS IPv6 no longer binds to temporary addresses as these can cause interruptions if they expire. - * Added additional hardening against address impersonation on networks (also in 1.6.6). - * Fix an issue that could cause clobbering of MacOS IP route settings on restart. - - * NOTE: Windows 7 is no longer supported! Windows 7 users will have to use version 1.6.5 or earlier. - -# 2021-09-15 -- Version 1.8.0 (preview release only) - - * A *completely* rewritten desktop UI for Mac and Windows! - * Implement a workaround for one potential source of a "coma" bug, which can occur if buggy NATs/routers stop allowing the service to communicate on a given port. ZeroTier now reassigns a new secondary port if it's offline for a while unless a secondary port is manually specified in local.conf. Working around crummy buggy routers is an ongoing effort. - * Fix for MacOS MTU capping issue on feth devices - * Fix for mistakenly using v6 source addresses for v4 routes on some platforms - * Stop binding to temporary IPv6 addresses - * Set MAC address before bringing up Linux TAP link - * Check if DNS servers need to be applied on macOS - * Upgrade json.hpp dependency to version 3.10.2 - -# 2021-09-21 -- Version 1.6.6 - - * Backport COM hash check mitigation against network member impersonation. - -# 2021-04-13 -- Version 1.6.5 - - * Fix a bug in potential network path filtering that could in some circumstances lead to "software laser" effects. - * Fix a printf overflow in zerotier-cli (not exploitable or a security risk) - * Windows now looks up the name of ZeroTier devices instead of relying on them having "ZeroTier" in them. - -# 2021-02-15 -- Version 1.6.4 - - * The groundhog saw his shadow, which meant that the "connection coma" bug still wasn't gone. We think we found it this time. - -# 2021-02-02 -- Version 1.6.3 - - * Likely fix for GitHub issue #1334, an issue that could cause ZeroTier to - go into a "coma" on some networks. - * Also groundhog day - -# 2020-11-30 -- Version 1.6.2 - - * Fix an ARM hardware AES crypto issue (not an exploitable vulnerability). - * Fix a Linux network leave hang due to a mutex deadlock. - -# 2020-11-24 -- Version 1.6.1 - -This release fixes some minor bugs and other issues in 1.6.0. - - * Fixed a bug that caused IP addresses in the 203.0.0.0/8 block to be miscategorized as not being in global scope. - * Changed Linux builds to (hopefully) fix LXC and SELinux issues. - * Fixed unaligned memory access that caused crash on FreeBSD systems on the ARM architecture. - * Merged CLI options for controlling bonded devices into the beta multipath code. - * Updated Windows driver with Microsoft cross-signing to fix issues on some Windows systems. - -# 2020-11-19 -- Version 1.6.0 - -Version 1.6.0 is a major release that incorporates back-ported features from the 2.0 branch, which is still under development. It also fixes a number of issues. - -New features and improvements (including those listed under 1.5.0): - - * **Apple Silicon** (MacOS ARM64) native support via universal binary. ZeroTier now requires the very latest Xcode to build. - * **Linux performance improvements** for up to 25% faster tun/tap I/O performance on multi-core systems. - * **Multipath support** with modes modeled after the Linux kernel's bonding driver. This includes active-passive and active-active modes with fast failover and load balancing. See section 2.1.5 of the manual. - * **DNS configuration** push from network controllers to end nodes, with locally configurable permissions for whether or not push is allowed. - * **AES-GMAC-SIV** encryption mode, which is both somewhat more secure and significantly faster than the old Salsa20/12-Poly1305 mode on hardware that supports AES acceleration. This includes virtually all X86-64 chips and most ARM64. This mode is based on AES-SIV and has been audited by Trail of Bits to ensure that it is equivalent security-wise. - -Bug fixes: - - * **Managed route assignment fixes** to eliminate missing routes on Linux and what we believe to be the source of sporadic high CPU usage on MacOS. - * **Hang on shutdown** issues should be fixed. - * **Sporadic multicast outages** should be fixed. - -Known remaining issues: - - * AES hardware acceleration is not yet supported on 32-bit ARM, PowerPC (32 or 64), or MIPS (32 or 64) systems. Currently supported are X86-64 and ARM64/AARCH64 with crypto extensions. - -# 2020-10-05 -- Version 1.5.0 (actually 1.6.0-beta1) - -Version 1.6.0 (1.5.0 is a beta!) is a significant release that incorporates a number of back-ported fixes and features from the ZeroTier 2.0 tree. - -Major new features are: - - * **Multipath support** with modes modeled after the Linux kernel's bonding driver. This includes active-passive and active-active modes with fast failover and load balancing. See section 2.1.5 of the manual. - * **DNS configuration** push from network controllers to end nodes, with locally configurable permissions for whether or not push is allowed. - * **AES-GMAC-SIV** encryption mode, which is both somewhat more secure and significantly faster than the old Salsa20/12-Poly1305 mode on hardware that supports AES acceleration. This includes virtually all X86-64 chips and most ARM64. This mode is based on AES-SIV and has been audited by Trail of Bits to ensure that it is equivalent security-wise. - -Known issues that are not yet fixed in this beta: - - * Some Mac users have reported periods of 100% CPU in kernel_task and connection instability after leaving networks that have been joined for a period of time, or needing to kill ZeroTier and restart it to finish leaving a network. This doesn't appear to affect all users and we haven't diagnosed the root cause yet. - * The service sometimes hangs on shutdown requiring a kill -9. This also does not affect all systems or users. - * AES hardware acceleration is not yet supported on 32-bit ARM, PowerPC (32 or 64), or MIPS (32 or 64) systems. Currently supported are X86-64 and ARM64/AARCH64 with crypto extensions. - * Some users have reported multicast/broadcast outages on networks lasting up to 30 seconds. Still investigating. - -We're trying to fix all these issues before the 1.6.0 release. Stay tuned. - -# 2019-08-30 -- Version 1.4.6 - - * Update default root list to latest - * ARM32 platform build and flag fixes - * Add a clarification line to LICENSE.txt - * Fix license message in CLI - * Windows service now looks for service command line arguments - * Fixed a bug that could cause excessive queued multicasts - -# 2019-08-23 -- Version 1.4.4 - - * Change license from GPL3 to BSL 1.1, see LICENSE.txt - * Fix an issue with the "ipauth" rule and auto-generated unforgeable IPv6 addresses - * Fix socket/bind errors setting IPs and routes on Linux - -# 2019-08-12 -- Version 1.4.2 - - * Fix high CPU use bug on some platforms - * Fix issues with PostgreSQL controller DB (only affects Central) - * Restore backward compatibility with MacOS versions prior to 10.13 - # 2019-07-29 -- Version 1.4.0 ### Major Changes @@ -372,7 +78,7 @@ We're trying to fix all these issues before the 1.6.0 release. Stay tuned. # 2017-04-20 -- Version 1.2.4 * Managed routes are now only bifurcated for the default route. This is a change in behavior, though few people will probably notice. Bifurcating all managed routes was causing more trouble than it was worth for most users. - * Up to 2X crypto speedup on x86-64 (except Windows, which will take some porting) and 32-bit ARM platforms due to integration of fast assembly language implementations of Salsa20/12 from the [supercop](http://bench.cr.yp.to/supercop.html) code base. These were written by Daniel J. Bernstein and are in the public domain. My MacBook Pro (Core i5 2.8ghz) now does almost 1.5GiB/sec Salsa20/12 per core and a Raspberry Pi got a 2X boost. 64-bit ARM support and Windows support will take some work but should not be too hard. + * Up to 2X crypto speedup on x86-64 (except Windows, which will take some porting) and 32-bit ARM platforms due to integration of fast assembly language implementations of Salsa20/12 from the [supercop](http://bench.cr.yp.to/supercop.html) code base. These were written by Daniel J. Bernstein and are in the public domain. My Macbook Pro (Core i5 2.8ghz) now does almost 1.5GiB/sec Salsa20/12 per core and a Raspberry Pi got a 2X boost. 64-bit ARM support and Windows support will take some work but should not be too hard. * Refactored code that manages credentials to greatly reduce memory use in most cases. This may also result in a small performance improvement. * Reworked and simplified path selection and priority logic to fix path instability and dead path persistence edge cases. There have been some sporadic reports of persistent path instabilities and dead paths hanging around that take minutes to resolve. These have proven difficult to reproduce in house, but hopefully this will fix them. In any case it seems to speed up path establishment in our tests and it makes the code simpler and more readable. * Eliminated some unused cruft from the code around path management and in the peer class. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index c539ed6a2..000000000 --- a/SECURITY.md +++ /dev/null @@ -1,93 +0,0 @@ -# Security - -ZeroTier takes the security of our software products and services seriously, which -includes all source code repositories managed through our GitHub organization. - -## Supported Versions - -The following versions of ZeroTier One receive security updates - -| Version | Supported | -| -------- | ------------------ | -| 1.14.x | :white_check_mark: | -| 1.12.x | :white_check_mark: | -| < 1.12.0 | :x: | - -## Reporting a Vulnerability - -**Please do not report security issues through public GitHub issues** - -Instead, please report vulnerabilities via email to security@zerotier.com. If possible, -please encrypt with our PGP key (see below). - -Please include the following information, or as much as you can provide to help us -understand the nature and scope of the issue: - - * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) - * Full paths of source file(s) related to the manifestation of the issue - * The location of the affected source code (tag/branch/commit or direct URL) - * Any special configuration required to reproduce the issue - * Step-by-step instructions to reproduce the issue - * Proof-of-concept or exploit code (if possible) - * Impact of the issue, including how an attacker might exploit the issue - -## Preferred Languages - -We prefer all communications to be in English. - -## security@zerotier.com PGP key - -``` ------BEGIN PGP PUBLIC KEY BLOCK----- - -mQINBGQGOVIBEACalXTnNqaiSOVLFEiqHpDMg8N/OI5D5850Xy1ZEvx3B3rz7cbn -k30ozHtJKbh+vqpyItE7DjyQAuF19gP5Q64Yh0Y+MmLHq60q/GwOwAYz7cI+UzA3 -5x8YqcmTp32LAM1xJn+iMlMLBuAmJl4kULKmOXPlpqPiyTFs5saizvm7fgRmfgJJ -HpsnIrTkaDFJhAR+jvMJohVYwmhuydeI0DsHu7KGpG1ddcHDrUjOPNqXnnAPSPwx -llw4yfKlQb8GYErsv/G5QVyzd5+SxEuiI4MARRnrk8LlMQ33CR6pzIQ/Bk5AAmye -mHqfEAknkiOf++urYhRs9BL3Kz3MdV0cg92zr9EFOg0u56jxf5OnAiTOhGUUA0hn -dS7peVGl46R9Oy2JYIazNDGi+4NIsYDFXsnsss9xOQVygPyeQd71zFHfix0jct9w -j3o/kj7Egsnm9nc13354bYT6bbalqXiRWwGH1eAFpjueNWiVFwZS6NZUP3WeNDiY -BlPo1LodvolbXiJcTILTCyEkERJPCK2zoE2nTdVfvTLWsuehw1M6Yd2/q74TVYy/ -RY+KjHkrChEBQ9PqXsXRHj6opKbT8JLfZkvU5k+3IiqqxOpB+QXFI/whj493CxWW -so7QAmzOCyJq8GDVPxzkwUac22YIkXdiOmb8i/HWq+kLY/HjQE259Gx6KwARAQAB -tClaZXJvVGllciBTZWN1cml0eSA8c2VjdXJpdHlAemVyb3RpZXIuY29tPokCTAQT -AQoANhYhBH1HQGb+4jzl6mnFqf09m6uqADkABQJkBjlSAhsDBAsJCAcEFQoJCAUW -AgMBAAIeAQIXgAAKCRD9PZurqgA5ACqPD/sFt6SG6Tu0HwTY2ofJtYsa2GBLL0pf -dYlX4cWSs1PVB5+m5Oj18y+GB2umA9GnsVtmvaSfp3XEngt2zNWX27uUsVfL35b2 -/5TVVe8RjzOedqMN+lQWMvO+f/C1zmWYXjjpC+iGjgMMaRRrofkkn+7uL4N9y6gY -rcXtpACT1rYFC+i1AKnZfUO8Vr5ji7odq0f7bDkN/N38rB0kRRwEmO8wqdpQK6gK -nxf9vgJl5ggimDk5Xtz1sfd3y28bf5N4hdOCkXUbd10nUFY3wDNTM4VxozxTGJeG -imdcc19Wuw/1fGUZ5SIjgPanCdPLGYwSTr+M6Fuern9uTtlC1GOby3BUtmVGP6EU -1pSAJSRpmoBPHKKOYtSMwV8PCboXru9P1ab8y8STKM3SKyghUJrl17gdc0LaksZa -E54pJudGPIQMFRqZjMdV6jgMuaLTozjZ4mW8EThf4mkX4xDkO8l7cOn0225ZYJZC -lZKpdnwzk9owkJA80u4KBNJxTtB4ZAPzjBsD5hFzCZQTLNQp/psU3EjZsau28eXT -E/C1QjEQHgy4ohkgQlCm1H1+clKssCWcdmsVGXuS1u8gh4K6X9b0Z6LeCGRaQvH2 -+DB8oTAdqp9nUZv9rP4pbo+sR4fF67CFLriVuxjedAiFkbM4uHMFcL4tc/X9+DRo -YN5X7oEkZvO507kCDQRkBjlSARAAz58UMF7K1qKyQjzKTcutaYZ5SaIGky9lCLZn -/2vjpFCoBogkxS/6IKQcwZk8b4S9QstaaQZDFEkxqNeKC0GiFTAMAb6SmYcK495h -EZnHl0NA5Nc2dBlZk5E/ENzTCz2bXaxCcVESc2z+xCzu07brbhGrqvliKiwOUzt9 -JzqEsar6I95OutBcZvkFCs44/Uf9bS1qf1w4klE8w3vdMtGH23umrET4tFZ+sh6o -ZFtQx0u2eKjsRdn/RMtsxLNaJlcE1DdIAqBpQrcmuwMC8v5wUGfCGZjhClzmyQlq -akUkayir7UtbHbFT/mgO+YI77YGXWk5QrwPscqqT2l8KB/YMujNDmaWa/0KV1lIY -zr5s4dzVeiwqFLR9ANFIhzFwzf3JLi6XSx123Qix0TxZoYPZCHl7yoi9qi6qybz5 -0Od2LSz3jbApeKYymZ+zjE+YV5y9DI6Wzy1j2M1FogNvTO9fMk+6dLt4HhTdSNvH -cKya462YCcy+tnZTkhmh+FTebbJlV6D4wG7skE5KCdBhjm53xLwp6XW9L6n2CrkL -W1IDBcCz0oPd1sMkXbO3wnxdXprV2XurCfsg/R2nszSNzvdJ8/xj3cr9hpoJ714R -qqyoEDRZ1Ss9kGL166o5MpN5qb/EewdkqGgWP7YFXbhsdHQiW7Z7dAqzjoaybD4O -nakkwyUAEQEAAYkCNgQYAQoAIBYhBH1HQGb+4jzl6mnFqf09m6uqADkABQJkBjlS -AhsMAAoJEP09m6uqADkAax0P/Rh8EZYRqW6dPYTl1YQusAK10rAcRNq3ekjofXGk -oXK1S7HWGoFgl5++5nfSfNgFJ5VLcgIM56wtIf49zFjWe5oC6fw8k+ghh4d2chMP -hdDILx6e0c30Iq1+EvovGR9hWa0wJ4cKTdzlwhY9ZC09q0ia+bl2mwpie1JQDR0c -zXCjt+PldLeeK9z1/XT0Q7KowYC+U18oR+KFm+EaRV4QT85JVequnIeGkmaHJrHB -lH4T5A5ib7y8edon1c0Zx3GsaxJUojkEJ0SX7ffVDu6ztUZfkHfCVpMW4VzUeGA/ -m+CtFO9ciLRGZEkRa+zhIGoBvwEXU0GiwiF4nZ0F2C8UioeW0YIEV9zl3nXJctYE -ZKc2whSENQRTGgaYHVoVZhznt71LKWgFLshwBo81UCXVkzwAjMW1ActDnmPw5M7q -xR5Qp5G49Z1GmfSozazha0HVFPKNV5i3RlTzs4yLUnZyH0yC9IvtOefMHcLjG96L -N5miEV97gvJJjrn8rhRvpUwAWgmT/9IuYjBNQTtNN40arto5HxezR76WCjdKYxdL -p3dM1iiBDShHNm7LdyZlLFhTOMU0tNBxJJ7B09ar5gakeZjD+2aB1ODX9VuFtozL -onBjI2gIkry0UIkuznHfFw05lZAZAiqHEVgVi/WTk4C/bklDZNgE0lx+IWzEz2iS -L455 -=lheL ------END PGP PUBLIC KEY BLOCK----- -``` diff --git a/attic/macui/ZeroTier One.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/attic/macui/ZeroTier One.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/attic/macui/ZeroTier One.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/attic/macui/ZeroTier One/ZeroTier One.entitlements b/attic/macui/ZeroTier One/ZeroTier One.entitlements deleted file mode 100644 index 0c67376eb..000000000 --- a/attic/macui/ZeroTier One/ZeroTier One.entitlements +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/attic/world/build.sh b/attic/world/build.sh index f3bcfabc9..aeb3b87fa 100755 --- a/attic/world/build.sh +++ b/attic/world/build.sh @@ -1,3 +1,3 @@ #!/bin/bash -c++ -std=c++11 -I../.. -I../../ext -I.. -g -o mkworld ../../node/C25519.cpp ../../node/Salsa20.cpp ../../node/SHA512.cpp ../../node/Identity.cpp ../../node/Utils.cpp ../../node/InetAddress.cpp ../../osdep/OSUtils.cpp mkworld.cpp -lm +c++ -std=c++11 -I../.. -I.. -O -o mkworld ../../node/C25519.cpp ../../node/Salsa20.cpp ../../node/SHA512.cpp ../../node/Identity.cpp ../../node/Utils.cpp ../../node/InetAddress.cpp ../../osdep/OSUtils.cpp mkworld.cpp -lm diff --git a/attic/world/mkworld.cpp b/attic/world/mkworld.cpp index 6b9bbe8d0..b8cb027b4 100644 --- a/attic/world/mkworld.cpp +++ b/attic/world/mkworld.cpp @@ -81,63 +81,44 @@ int main(int argc,char **argv) std::vector roots; const uint64_t id = ZT_WORLD_ID_EARTH; - const uint64_t ts = 1567191349589ULL; // August 30th, 2019 + const uint64_t ts = 1562631342273ULL; // July 8th, 2019 - // Los Angeles roots.push_back(World::Root()); roots.back().identity = Identity("3a46f1bf30:0:76e66fab33e28549a62ee2064d1843273c2c300ba45c3f20bef02dbad225723bb59a9bb4b13535730961aeecf5a163ace477cceb0727025b99ac14a5166a09a3"); roots.back().stableEndpoints.push_back(InetAddress("185.180.13.82/9993")); roots.back().stableEndpoints.push_back(InetAddress("2a02:6ea0:c815::/9993")); - // Miami - roots.push_back(World::Root()); - roots.back().identity = Identity("de8950a8b2:0:1b3ada8251b91b6b6fa6535b8c7e2460918f4f729abdec97d3c7f3796868fb02f0de0b0ee554b2d59fc3524743eebfcf5315e790ed6d92db5bd10c28c09b40ef"); - roots.back().stableEndpoints.push_back(InetAddress("207.246.73.245/443")); - roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:9002:5cb:ec4:7aff:fe8f:69d9/443")); - - // Tokyo - roots.push_back(World::Root()); - roots.back().identity = Identity("34e0a5e174:0:93efb50934788f856d5cfb9ca5be88e85b40965586b75befac900df77352c145a1ba7007569d37c77bfe52c0999f3bdc67a47a4a6000b720a883ce47aa2fb7f8"); - roots.back().stableEndpoints.push_back(InetAddress("147.75.92.2/443")); - roots.back().stableEndpoints.push_back(InetAddress("2604:1380:3000:7100::1/443")); - - // Amsterdam - roots.push_back(World::Root()); - roots.back().identity = Identity("992fcf1db7:0:206ed59350b31916f749a1f85dffb3a8787dcbf83b8c6e9448d4e3ea0e3369301be716c3609344a9d1533850fb4460c50af43322bcfc8e13d3301a1f1003ceb6"); - roots.back().stableEndpoints.push_back(InetAddress("195.181.173.159/443")); - roots.back().stableEndpoints.push_back(InetAddress("2a02:6ea0:c024::/443")); - // Alice - //roots.push_back(World::Root()); - //roots.back().identity = Identity("9d219039f3:0:01f0922a98e3b34ebcbff333269dc265d7a020aab69d72be4d4acc9c8c9294785771256cd1d942a90d1bd1d2dca3ea84ef7d85afe6611fb43ff0b74126d90a6e"); - //roots.back().stableEndpoints.push_back(InetAddress("188.166.94.177/9993")); // Amsterdam - //roots.back().stableEndpoints.push_back(InetAddress("2a03:b0c0:2:d0::7d:1/9993")); // Amsterdam - //roots.back().stableEndpoints.push_back(InetAddress("154.66.197.33/9993")); // Johannesburg - //roots.back().stableEndpoints.push_back(InetAddress("2c0f:f850:154:197::33/9993")); // Johannesburg - //roots.back().stableEndpoints.push_back(InetAddress("159.203.97.171/9993")); // New York - //roots.back().stableEndpoints.push_back(InetAddress("2604:a880:800:a1::54:6001/9993")); // New York - //roots.back().stableEndpoints.push_back(InetAddress("131.255.6.16/9993")); // Buenos Aires - //roots.back().stableEndpoints.push_back(InetAddress("2803:eb80:0:e::2/9993")); // Buenos Aires - //roots.back().stableEndpoints.push_back(InetAddress("107.170.197.14/9993")); // San Francisco - //roots.back().stableEndpoints.push_back(InetAddress("2604:a880:1:20::200:e001/9993")); // San Francisco - //roots.back().stableEndpoints.push_back(InetAddress("128.199.197.217/9993")); // Singapore - //roots.back().stableEndpoints.push_back(InetAddress("2400:6180:0:d0::b7:4001/9993")); // Singapore + roots.push_back(World::Root()); + roots.back().identity = Identity("9d219039f3:0:01f0922a98e3b34ebcbff333269dc265d7a020aab69d72be4d4acc9c8c9294785771256cd1d942a90d1bd1d2dca3ea84ef7d85afe6611fb43ff0b74126d90a6e"); + roots.back().stableEndpoints.push_back(InetAddress("188.166.94.177/9993")); // Amsterdam + roots.back().stableEndpoints.push_back(InetAddress("2a03:b0c0:2:d0::7d:1/9993")); // Amsterdam + roots.back().stableEndpoints.push_back(InetAddress("154.66.197.33/9993")); // Johannesburg + roots.back().stableEndpoints.push_back(InetAddress("2c0f:f850:154:197::33/9993")); // Johannesburg + roots.back().stableEndpoints.push_back(InetAddress("159.203.97.171/9993")); // New York + roots.back().stableEndpoints.push_back(InetAddress("2604:a880:800:a1::54:6001/9993")); // New York + roots.back().stableEndpoints.push_back(InetAddress("131.255.6.16/9993")); // Buenos Aires + roots.back().stableEndpoints.push_back(InetAddress("2803:eb80:0:e::2/9993")); // Buenos Aires + roots.back().stableEndpoints.push_back(InetAddress("107.170.197.14/9993")); // San Francisco + roots.back().stableEndpoints.push_back(InetAddress("2604:a880:1:20::200:e001/9993")); // San Francisco + roots.back().stableEndpoints.push_back(InetAddress("128.199.197.217/9993")); // Singapore + roots.back().stableEndpoints.push_back(InetAddress("2400:6180:0:d0::b7:4001/9993")); // Singapore // Bob - //roots.push_back(World::Root()); - //roots.back().identity = Identity("8841408a2e:0:bb1d31f2c323e264e9e64172c1a74f77899555ed10751cd56e86405cde118d02dffe555d462ccf6a85b5631c12350c8d5dc409ba10b9025d0f445cf449d92b1c"); - //roots.back().stableEndpoints.push_back(InetAddress("45.32.198.130/9993")); // Dallas - //roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:6400:81c3:5400:00ff:fe18:1d61/9993")); // Dallas - //roots.back().stableEndpoints.push_back(InetAddress("46.101.160.249/9993")); // Frankfurt - //roots.back().stableEndpoints.push_back(InetAddress("2a03:b0c0:3:d0::6a:3001/9993")); // Frankfurt - //roots.back().stableEndpoints.push_back(InetAddress("107.191.46.210/9993")); // Paris - //roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:6800:83a4::64/9993")); // Paris - //roots.back().stableEndpoints.push_back(InetAddress("45.32.246.179/9993")); // Sydney - //roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:5800:8bf8:5400:ff:fe15:b39a/9993")); // Sydney - //roots.back().stableEndpoints.push_back(InetAddress("45.32.248.87/9993")); // Tokyo - //roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:7000:9bc9:5400:00ff:fe15:c4f5/9993")); // Tokyo - //roots.back().stableEndpoints.push_back(InetAddress("159.203.2.154/9993")); // Toronto - //roots.back().stableEndpoints.push_back(InetAddress("2604:a880:cad:d0::26:7001/9993")); // Toronto + roots.push_back(World::Root()); + roots.back().identity = Identity("8841408a2e:0:bb1d31f2c323e264e9e64172c1a74f77899555ed10751cd56e86405cde118d02dffe555d462ccf6a85b5631c12350c8d5dc409ba10b9025d0f445cf449d92b1c"); + roots.back().stableEndpoints.push_back(InetAddress("45.32.198.130/9993")); // Dallas + roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:6400:81c3:5400:00ff:fe18:1d61/9993")); // Dallas + roots.back().stableEndpoints.push_back(InetAddress("46.101.160.249/9993")); // Frankfurt + roots.back().stableEndpoints.push_back(InetAddress("2a03:b0c0:3:d0::6a:3001/9993")); // Frankfurt + roots.back().stableEndpoints.push_back(InetAddress("107.191.46.210/9993")); // Paris + roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:6800:83a4::64/9993")); // Paris + roots.back().stableEndpoints.push_back(InetAddress("45.32.246.179/9993")); // Sydney + roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:5800:8bf8:5400:ff:fe15:b39a/9993")); // Sydney + roots.back().stableEndpoints.push_back(InetAddress("45.32.248.87/9993")); // Tokyo + roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:7000:9bc9:5400:00ff:fe15:c4f5/9993")); // Tokyo + roots.back().stableEndpoints.push_back(InetAddress("159.203.2.154/9993")); // Toronto + roots.back().stableEndpoints.push_back(InetAddress("2604:a880:cad:d0::26:7001/9993")); // Toronto // END WORLD DEFINITION // ========================================================================= diff --git a/attic/world/world.bin b/attic/world/world.bin index 88049ccdc..fff7e2a99 100644 Binary files a/attic/world/world.bin and b/attic/world/world.bin differ diff --git a/ci/Dockerfile-build.deb b/ci/Dockerfile-build.deb deleted file mode 100644 index 09ed717f8..000000000 --- a/ci/Dockerfile-build.deb +++ /dev/null @@ -1,13 +0,0 @@ -ARG ZT_NAME -FROM 084037375216.dkr.ecr.us-east-2.amazonaws.com/${ZT_NAME}-builder as builder -WORKDIR /work/build -COPY . . -RUN pwd -RUN ls -la . -RUN make clean -RUN make debian -RUN ls -ls /work - -FROM scratch AS export -ARG ZT_NAME -COPY --from=builder /work/*.deb ./${ZT_NAME}/ diff --git a/ci/Dockerfile-build.el6 b/ci/Dockerfile-build.el6 deleted file mode 100644 index c5848c984..000000000 --- a/ci/Dockerfile-build.el6 +++ /dev/null @@ -1,36 +0,0 @@ -ARG DOCKER_ARCH -FROM --platform=linux/${DOCKER_ARCH} alpine:edge AS builder - -RUN apk update -RUN apk add curl -RUN apk add bash -RUN apk add file -RUN apk add rust -RUN apk add cargo -RUN apk add make -RUN apk add cmake -RUN apk add clang -RUN apk add openssl-dev -RUN apk add linux-headers -RUN apk add build-base -RUN apk add openssl-libs-static - -COPY . . -RUN ZT_STATIC=1 make -RUN ls -la - -ARG DOCKER_ARCH -FROM --platform=linux/${DOCKER_ARCH} centos:6 AS stage -WORKDIR /root/rpmbuild/BUILD -COPY . . -COPY --from=builder zerotier-* ./ -RUN curl https://gist.githubusercontent.com/someara/b363002ba6e57b3c474dd027d4daef85/raw/4ac5534139752fc92fbe1a53599a390214f69615/el6%2520vault --output /etc/yum.repos.d/CentOS-Base.repo -RUN uname -a -RUN yum -y install make gcc rpm-build -RUN pwd -RUN ls -la -RUN make redhat - -FROM scratch AS export -ARG ZT_NAME -COPY --from=stage /root/rpmbuild/RPMS/*/*.rpm ./${ZT_NAME}/ diff --git a/ci/Dockerfile-build.rpm b/ci/Dockerfile-build.rpm deleted file mode 100644 index cab00a06d..000000000 --- a/ci/Dockerfile-build.rpm +++ /dev/null @@ -1,9 +0,0 @@ -ARG ZT_NAME -FROM 084037375216.dkr.ecr.us-east-2.amazonaws.com/${ZT_NAME}-builder as builder -WORKDIR /root/rpmbuild/BUILD -COPY . . -RUN make redhat - -FROM scratch AS export -ARG ZT_NAME -COPY --from=builder /root/rpmbuild/RPMS/*/*.rpm ./${ZT_NAME}/ diff --git a/ci/Dockerfile-test.deb b/ci/Dockerfile-test.deb deleted file mode 100644 index 7c709a863..000000000 --- a/ci/Dockerfile-test.deb +++ /dev/null @@ -1,13 +0,0 @@ -ARG ZT_NAME -FROM 084037375216.dkr.ecr.us-east-2.amazonaws.com/${ZT_NAME}-tester -ARG BASEURL -ARG VERSION -ARG DEB_ARCH -ARG ZT_NAME -ARG DISTRO -RUN curl -s http://${BASEURL}/key.gpg -o /etc/apt/trusted.gpg.d/zerotier.gpg -RUN echo "deb [arch=${DEB_ARCH} signed-by=/etc/apt/trusted.gpg.d/zerotier.gpg] http://${BASEURL}/${DISTRO} ${ZT_NAME} main" > /etc/apt/sources.list.d/zerotier.list -RUN apt-get -qq update -RUN apt-get -qq install zerotier-one=${VERSION} - -RUN ldd $(which zerotier-cli) diff --git a/ci/Dockerfile-test.el6 b/ci/Dockerfile-test.el6 deleted file mode 100644 index 499667313..000000000 --- a/ci/Dockerfile-test.el6 +++ /dev/null @@ -1,4 +0,0 @@ -ARG DOCKER_ARCH -FROM --platform=linux/${DOCKER_ARCH} centos:6 -RUN printf "[C6.10-base]\nname=CentOS-6.10 - Base\nbaseurl=http://vault.epel.cloud/6.10/os/\$basearch/\ngpgcheck=1\ngpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6\nenabled=1\nmetadata_expire=never\n" > /etc/yum.repos.d/CentOS-Base.repo -RUN yum -y install curl diff --git a/ci/Dockerfile-test.rpm b/ci/Dockerfile-test.rpm deleted file mode 100644 index 6a98607b9..000000000 --- a/ci/Dockerfile-test.rpm +++ /dev/null @@ -1,17 +0,0 @@ -ARG ZT_NAME -FROM 084037375216.dkr.ecr.us-east-2.amazonaws.com/${ZT_NAME}-tester -ARG BASEURL -ARG VERSION -ARG DEB_ARCH -ARG ZT_NAME -ARG DISTRO -ARG DNF_ARCH -RUN curl -s http://${BASEURL}/key.asc -o /etc/pki/rpm-gpg/RPM-GPG-KEY-zerotier -RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-zerotier -RUN rpm -q gpg-pubkey --qf '%{NAME}-%{VERSION}-%{RELEASE}\t%{SUMMARY}\n' -RUN printf "[zerotier]\nname=zerotier\nbaseurl=http://${BASEURL}/${DISTRO}/${ZT_NAME}/$basearch/\nenabled=1\ngpgcheck=1\ngpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-zerotier\n" > /etc/yum.repos.d/zerotier.repo - -# RUN yum -v repolist -RUN setarch ${DNF_ARCH} yum -y install zerotier-one-${VERSION} -RUN file $(which zerotier-cli) -RUN ldd $(which zerotier-cli) diff --git a/ci/scripts/build.sh b/ci/scripts/build.sh deleted file mode 100755 index a4b8ca94b..000000000 --- a/ci/scripts/build.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -set -euo pipefail -IFS=$'\n\t' - -ZT_NAME="$1" ; shift -DISTRO="$1" ; shift -ZT_ISA="$1" ; shift -VERSION="$1" ; shift -BUILD_EVENT="$1" ; shift - -source "$(dirname $0)/lib.sh" - -if [ -f "ci/Dockerfile-build.${ZT_NAME}" ]; then - DOCKERFILE="ci/Dockerfile-build.${ZT_NAME}" -else - DOCKERFILE="ci/Dockerfile-build.${PKGFMT}" -fi - -echo "#~~~~~~~~~~~~~~~~~~~~" -echo "$0 variables:" -echo "nproc: $(nproc)" -echo "ZT_NAME: ${ZT_NAME}" -echo "DISTRO: ${DISTRO}" -echo "ZT_ISA: ${ZT_ISA}" -echo "VERSION: ${VERSION}" -echo "BUILD_EVENT: ${BUILD_EVENT}" -echo "DOCKER_ARCH: ${DOCKER_ARCH}" -echo "DNF_ARCH: ${DNF_ARCH}" -echo "RUST_TRIPLET: ${RUST_TRIPLET}" -echo "PKGFMT: ${PKGFMT}" -echo "PWD: ${PWD}" -echo "DOCKERFILE: ${DOCKERFILE}" -echo "#~~~~~~~~~~~~~~~~~~~~" - -make munge_rpm zerotier-one.spec VERSION=${VERSION} -make munge_deb debian/changelog VERSION=${VERSION} - -docker buildx build \ - --no-cache=true \ - --build-arg ZT_NAME="${ZT_NAME}" \ - --build-arg RUST_TRIPLET="${RUST_TRIPLET}" \ - --build-arg DOCKER_ARCH="${DOCKER_ARCH}" \ - --build-arg DNF_ARCH="${DNF_ARCH}" \ - --platform linux/${DOCKER_ARCH} \ - -f ${DOCKERFILE} \ - -t build \ - . \ - --output type=local,dest=. \ - --target export diff --git a/ci/scripts/lib.sh b/ci/scripts/lib.sh deleted file mode 100755 index 43c35762b..000000000 --- a/ci/scripts/lib.sh +++ /dev/null @@ -1,63 +0,0 @@ - -case $ZT_NAME in - el*|fc*|amzn*) - export PKGFMT=rpm - ;; - *) - export PKGFMT=deb -esac - -case $ZT_ISA in - 386) - export DOCKER_ARCH=386 - export DEB_ARCH=i386 - export DNF_ARCH=i686 - export RUST_TRIPLET=i686-unknown-linux-gnu - ;; - amd64) - export DOCKER_ARCH=amd64 - export DEB_ARCH=amd64 - export DNF_ARCH=x86_64 - export RUST_TRIPLET=x86_64-unknown-linux-gnu - ;; - armv7) - export DOCKER_ARCH=arm/v7 - export DNF_ARCH=armv7 - export DEB_ARCH=armhf - export RUST_TRIPLET=armv7-unknown-linux-gnueabihf - ;; - arm64) - export DOCKER_ARCH=arm64/v8 - export DEB_ARCH=arm64 - export DNF_ARCH=linux64 - export RUST_TRIPLET=aarch64-unknown-linux-gnu - ;; - riscv64) - export DOCKER_ARCH=riscv64 - export DEB_ARCH=riscv64 - export DNF_ARCH=riscv64 - export RUST_TRIPLET=riscv64gc-unknown-linux-gnu - ;; - ppc64le) - export DOCKER_ARCH=ppc64le - export DEB_ARCH=ppc64el - export DNF_ARCH=ppc64le - export RUST_TRIPLET=powerpc64le-unknown-linux-gnu - ;; - mips64le) - export DOCKER_ARCH=mips64le - export DEB_ARCH=mips64le - export DNF_ARCH=mips64le - export RUST_TRIPLET=mips64el-unknown-linux-gnuabi64 - ;; - s390x) - export DOCKER_ARCH=s390x - export DEB_ARCH=s390x - export DNF_ARCH=s390x - export RUST_TRIPLET=s390x-unknown-linux-gnu - ;; - *) - echo "ERROR: could not determine architecture settings. PLEASE FIX ME" - exit 1 - ;; -esac diff --git a/ci/scripts/munge_debian_changelog.sh b/ci/scripts/munge_debian_changelog.sh deleted file mode 100755 index d37cb0638..000000000 --- a/ci/scripts/munge_debian_changelog.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -set -euo pipefail -IFS=$'\n\t' - -export FILE=$1 -export VERSION=$2 -export NAME=$3 -export MESSAGE=$4 -export DATE=$(date "+%a, %d %b %Y %T %z") -# export DATE=$(date "+%a %b %d %Y") - -set +e -grep --version | grep BSD &> /dev/null -if [ $? == 0 ]; then BSDGREP=true ; else BSDGREP=false ; fi -set -e - -# echo "#~~~~~~~~~~~~~~~~~~~~" -# echo "$0 variables:" -# echo "VERSION: ${VERSION}" -# echo "NAME: ${NAME}" -# echo "MESSAGE: ${MESSAGE}" -# echo "DATE: ${DATE}" -# echo "BSDGREP: ${BSDGREP}" -# echo "#~~~~~~~~~~~~~~~~~~~~" -# echo - -if $BSDGREP ; then - sed -i '' s/^Version:.*/"Version: ${VERSION}"/ ${FILE} -else - sed -i s/^Version:.*/"Version: ${VERSION}"/ ${FILE} -fi - -awk -v version=${VERSION} -v date=${DATE} -v name=${NAME} -v message=${MESSAGE} \ - 'BEGIN{print "zerotier-one (" version ") stable; urgency=medium\n\n * " message "\n\n -- " name " " date "\n" }{ print }' \ - ${FILE} > ${FILE}.new - -mv ${FILE}.new ${FILE} diff --git a/ci/scripts/munge_rpm_spec.sh b/ci/scripts/munge_rpm_spec.sh deleted file mode 100755 index 289df1ed4..000000000 --- a/ci/scripts/munge_rpm_spec.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -set -euo pipefail -IFS=$'\n\t' - -export FILE=$1 -export VERSION=$2 -export NAME=$3 -export MESSAGE=$4 -export DATE=$(date "+%a %b %d %Y") - -set +e -grep --version | grep BSD &> /dev/null -if [ $? == 0 ]; then BSDGREP=true ; else BSDGREP=false ; fi -set -e - -# echo "#~~~~~~~~~~~~~~~~~~~~" -# echo "$0 variables:" -# echo "VERSION: ${VERSION}" -# echo "NAME: ${NAME}" -# echo "MESSAGE: ${MESSAGE}" -# echo "DATE: ${DATE}" -# echo "BSDGREP: ${BSDGREP}" -# echo "#~~~~~~~~~~~~~~~~~~~~" -# echo - -if $BSDGREP ; then - sed -i '' s/^Version:.*/"Version: ${VERSION}"/ ${FILE} -else - sed -i s/^Version:.*/"Version: ${VERSION}"/ ${FILE} -fi - -awk -v version=${VERSION} -v date=${DATE} -v name=${NAME} -v message=${MESSAGE} \ - 'FNR==NR{ if (/%changelog/) p=NR; next} 1; FNR==p{ print "* " date " " name " - " version "\n- " message "\n" }' \ - ${FILE} ${FILE} > ${FILE}.new - -mv ${FILE}.new ${FILE} diff --git a/ci/scripts/publish.sh b/ci/scripts/publish.sh deleted file mode 100755 index fa1e2468a..000000000 --- a/ci/scripts/publish.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -set -euo pipefail -IFS=$'\n\t' - -ZT_NAME="$1" ; shift -DISTRO="$1" ; shift -ZT_ISA="$1" ; shift -VERSION="$1" ; shift -BUILD_EVENT="$1" ; shift - -source "$(dirname $0)/lib.sh" - -if [ ${BUILD_EVENT} == "tag" ]; then - CHANNEL="zerotier-releases" -else - CHANNEL="zerotier-builds" -fi - -function publish_rpm { - mkdir -p /${CHANNEL}/${DISTRO} - ls -la /${CHANNEL} - ls -la . - cp -a ${ZT_NAME} /${CHANNEL}/${DISTRO} -} - -function publish_deb { - mkdir -p /${CHANNEL}/${DISTRO}/pool/dists/${ZT_NAME}/main - cp -a ${ZT_NAME}/* /${CHANNEL}/${DISTRO}/pool/dists/${ZT_NAME}/main -} - -case ${PKGFMT} in - "rpm") - publish_rpm - ;; - "deb") - publish_deb -esac - diff --git a/ci/scripts/test.sh b/ci/scripts/test.sh deleted file mode 100755 index 931e9bb91..000000000 --- a/ci/scripts/test.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash -set -euo pipefail -IFS=$'\n\t' - -ZT_NAME="$1" ; shift -DISTRO="$1" ; shift -ZT_ISA="$1" ; shift -VERSION="$1" ; shift -BUILD_EVENT="$1" ; shift - -source "$(dirname $0)/lib.sh" - -if [ -f "ci/Dockerfile-test.${ZT_NAME}" ]; then - DOCKERFILE="ci/Dockerfile-test.${ZT_NAME}" -else - DOCKERFILE="ci/Dockerfile-test.${PKGFMT}" -fi - -if [ ${BUILD_EVENT} == "tag" ]; then - BASEURL="zerotier-releases.home.arpa" -else - BASEURL="zerotier-builds.home.arpa" -fi - -echo "#~~~~~~~~~~~~~~~~~~~~" -echo "$0 variables:" -echo "nproc: $(nproc)" -echo "ZT_NAME: ${ZT_NAME}" -echo "DISTRO: ${DISTRO}" -echo "ZT_ISA: ${ZT_ISA}" -echo "VERSION: ${VERSION}" -echo "BUILD_EVENT: ${BUILD_EVENT}" -echo "DOCKER_ARCH: ${DOCKER_ARCH}" -echo "DNF_ARCH: ${DNF_ARCH}" -echo "RUST_TRIPLET: ${RUST_TRIPLET}" -echo "PKGFMT: ${PKGFMT}" -echo "PWD: ${PWD}" -echo "DOCKERFILE: ${DOCKERFILE}" -echo "#~~~~~~~~~~~~~~~~~~~~" - -# docker pull -q --platform="linux/${DOCKER_ARCH}" 084037375216.dkr.ecr.us-east-2.amazonaws.com/${ZT_NAME}-tester - -docker buildx build \ - --build-arg BASEURL="${BASEURL}" \ - --build-arg ZT_NAME="${ZT_NAME}" \ - --build-arg DISTRO="${DISTRO}" \ - --build-arg DEB_ARCH="${DEB_ARCH}" \ - --build-arg DNF_ARCH="${DNF_ARCH}" \ - --build-arg VERSION="${VERSION}" \ - --build-arg DOCKER_ARCH="${DOCKER_ARCH}" \ - --platform "linux/${DOCKER_ARCH}" \ - --no-cache \ - -f ${DOCKERFILE} \ - -t test \ - . diff --git a/controller/CV1.cpp b/controller/CV1.cpp deleted file mode 100644 index 920ed7ec8..000000000 --- a/controller/CV1.cpp +++ /dev/null @@ -1,1952 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#include "CV1.hpp" - -#ifdef ZT_CONTROLLER_USE_LIBPQ - -#include "../node/Constants.hpp" -#include "../node/SHA512.hpp" -#include "../version.h" -#include "CtlUtil.hpp" -#include "EmbeddedNetworkController.hpp" -#include "Redis.hpp" - -#include -#include -#include -#include -#include -#include - -// #define REDIS_TRACE 1 - -using json = nlohmann::json; - -namespace { - -static const int DB_MINIMUM_VERSION = 38; - -} // anonymous namespace - -using namespace ZeroTier; - -using Attrs = std::vector >; -using Item = std::pair; -using ItemStream = std::vector; - -CV1::CV1(const Identity& myId, const char* path, int listenPort, RedisConfig* rc) - : DB() - , _pool() - , _myId(myId) - , _myAddress(myId.address()) - , _ready(0) - , _connected(1) - , _run(1) - , _waitNoticePrinted(false) - , _listenPort(listenPort) - , _rc(rc) - , _redis(NULL) - , _cluster(NULL) - , _redisMemberStatus(false) - , _smee(NULL) -{ - char myAddress[64]; - _myAddressStr = myId.address().toString(myAddress); - _connString = std::string(path); - auto f = std::make_shared(_connString); - _pool = std::make_shared >(15, 5, std::static_pointer_cast(f)); - - memset(_ssoPsk, 0, sizeof(_ssoPsk)); - char* const ssoPskHex = getenv("ZT_SSO_PSK"); -#ifdef ZT_TRACE - fprintf(stderr, "ZT_SSO_PSK: %s\n", ssoPskHex); -#endif - if (ssoPskHex) { - // SECURITY: note that ssoPskHex will always be null-terminated if libc actually - // returns something non-NULL. If the hex encodes something shorter than 48 bytes, - // it will be padded at the end with zeroes. If longer, it'll be truncated. - Utils::unhex(ssoPskHex, _ssoPsk, sizeof(_ssoPsk)); - } - const char* redisMemberStatus = getenv("ZT_REDIS_MEMBER_STATUS"); - if (redisMemberStatus && (strcmp(redisMemberStatus, "true") == 0)) { - _redisMemberStatus = true; - fprintf(stderr, "Using redis for member status\n"); - } - - auto c = _pool->borrow(); - pqxx::work txn { *c->c }; - - pqxx::row r { txn.exec1("SELECT version FROM ztc_database") }; - int dbVersion = r[0].as(); - txn.commit(); - - if (dbVersion < DB_MINIMUM_VERSION) { - fprintf(stderr, "Central database schema version too low. This controller version requires a minimum schema version of %d. Please upgrade your Central instance", DB_MINIMUM_VERSION); - exit(1); - } - _pool->unborrow(c); - - if (_rc != NULL) { - sw::redis::ConnectionOptions opts; - sw::redis::ConnectionPoolOptions poolOpts; - opts.host = _rc->hostname; - opts.port = _rc->port; - opts.password = _rc->password; - opts.db = 0; - opts.keep_alive = true; - opts.connect_timeout = std::chrono::seconds(3); - poolOpts.size = 25; - poolOpts.wait_timeout = std::chrono::seconds(5); - poolOpts.connection_lifetime = std::chrono::minutes(3); - poolOpts.connection_idle_time = std::chrono::minutes(1); - if (_rc->clusterMode) { - fprintf(stderr, "Using Redis in Cluster Mode\n"); - _cluster = std::make_shared(opts, poolOpts); - } - else { - fprintf(stderr, "Using Redis in Standalone Mode\n"); - _redis = std::make_shared(opts, poolOpts); - } - } - - _readyLock.lock(); - - fprintf(stderr, "[%s] NOTICE: %.10llx controller PostgreSQL waiting for initial data download..." ZT_EOL_S, ::_timestr(), (unsigned long long)_myAddress.toInt()); - _waitNoticePrinted = true; - - initializeNetworks(); - initializeMembers(); - - _heartbeatThread = std::thread(&CV1::heartbeat, this); - _membersDbWatcher = std::thread(&CV1::membersDbWatcher, this); - _networksDbWatcher = std::thread(&CV1::networksDbWatcher, this); - for (int i = 0; i < ZT_CENTRAL_CONTROLLER_COMMIT_THREADS; ++i) { - _commitThread[i] = std::thread(&CV1::commitThread, this); - } - _onlineNotificationThread = std::thread(&CV1::onlineNotificationThread, this); - - configureSmee(); -} - -CV1::~CV1() -{ - if (_smee != NULL) { - smeeclient::smee_client_delete(_smee); - _smee = NULL; - } - - _run = 0; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - _heartbeatThread.join(); - _membersDbWatcher.join(); - _networksDbWatcher.join(); - _commitQueue.stop(); - for (int i = 0; i < ZT_CENTRAL_CONTROLLER_COMMIT_THREADS; ++i) { - _commitThread[i].join(); - } - _onlineNotificationThread.join(); -} - -void CV1::configureSmee() -{ - const char* TEMPORAL_SCHEME = "ZT_TEMPORAL_SCHEME"; - const char* TEMPORAL_HOST = "ZT_TEMPORAL_HOST"; - const char* TEMPORAL_PORT = "ZT_TEMPORAL_PORT"; - const char* TEMPORAL_NAMESPACE = "ZT_TEMPORAL_NAMESPACE"; - const char* SMEE_TASK_QUEUE = "ZT_SMEE_TASK_QUEUE"; - - const char* scheme = getenv(TEMPORAL_SCHEME); - if (scheme == NULL) { - scheme = "http"; - } - const char* host = getenv(TEMPORAL_HOST); - const char* port = getenv(TEMPORAL_PORT); - const char* ns = getenv(TEMPORAL_NAMESPACE); - const char* task_queue = getenv(SMEE_TASK_QUEUE); - - if (scheme != NULL && host != NULL && port != NULL && ns != NULL && task_queue != NULL) { - fprintf(stderr, "creating smee client\n"); - std::string hostPort = std::string(scheme) + std::string("://") + std::string(host) + std::string(":") + std::string(port); - this->_smee = smeeclient::smee_client_new(hostPort.c_str(), ns, task_queue); - } - else { - fprintf(stderr, "Smee client not configured\n"); - } -} - -bool CV1::waitForReady() -{ - while (_ready < 2) { - _readyLock.lock(); - _readyLock.unlock(); - } - return true; -} - -bool CV1::isReady() -{ - return ((_ready == 2) && (_connected)); -} - -bool CV1::save(nlohmann::json& record, bool notifyListeners) -{ - bool modified = false; - try { - if (! record.is_object()) { - fprintf(stderr, "record is not an object?!?\n"); - return false; - } - const std::string objtype = record["objtype"]; - if (objtype == "network") { - // fprintf(stderr, "network save\n"); - const uint64_t nwid = OSUtils::jsonIntHex(record["id"], 0ULL); - if (nwid) { - nlohmann::json old; - get(nwid, old); - if ((! old.is_object()) || (! _compareRecords(old, record))) { - record["revision"] = OSUtils::jsonInt(record["revision"], 0ULL) + 1ULL; - _commitQueue.post(std::pair(record, notifyListeners)); - modified = true; - } - } - } - else if (objtype == "member") { - std::string networkId = record["nwid"]; - std::string memberId = record["id"]; - const uint64_t nwid = OSUtils::jsonIntHex(record["nwid"], 0ULL); - const uint64_t id = OSUtils::jsonIntHex(record["id"], 0ULL); - // fprintf(stderr, "member save %s-%s\n", networkId.c_str(), memberId.c_str()); - if ((id) && (nwid)) { - nlohmann::json network, old; - get(nwid, network, id, old); - if ((! old.is_object()) || (! _compareRecords(old, record))) { - // fprintf(stderr, "commit queue post\n"); - record["revision"] = OSUtils::jsonInt(record["revision"], 0ULL) + 1ULL; - _commitQueue.post(std::pair(record, notifyListeners)); - modified = true; - } - else { - // fprintf(stderr, "no change\n"); - } - } - } - else { - fprintf(stderr, "uhh waaat\n"); - } - } - catch (std::exception& e) { - fprintf(stderr, "Error on PostgreSQL::save: %s\n", e.what()); - } - catch (...) { - fprintf(stderr, "Unknown error on PostgreSQL::save\n"); - } - return modified; -} - -void CV1::eraseNetwork(const uint64_t networkId) -{ - fprintf(stderr, "PostgreSQL::eraseNetwork\n"); - char tmp2[24]; - waitForReady(); - Utils::hex(networkId, tmp2); - std::pair tmp; - tmp.first["id"] = tmp2; - tmp.first["objtype"] = "_delete_network"; - tmp.second = true; - _commitQueue.post(tmp); - nlohmann::json nullJson; - _networkChanged(tmp.first, nullJson, true); -} - -void CV1::eraseMember(const uint64_t networkId, const uint64_t memberId) -{ - fprintf(stderr, "PostgreSQL::eraseMember\n"); - char tmp2[24]; - waitForReady(); - std::pair tmp, nw; - Utils::hex(networkId, tmp2); - tmp.first["nwid"] = tmp2; - Utils::hex(memberId, tmp2); - tmp.first["id"] = tmp2; - tmp.first["objtype"] = "_delete_member"; - tmp.second = true; - _commitQueue.post(tmp); - nlohmann::json nullJson; - _memberChanged(tmp.first, nullJson, true); -} - -void CV1::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress, const char* osArch) -{ - std::lock_guard l(_lastOnline_l); - NodeOnlineRecord& i = _lastOnline[std::pair(networkId, memberId)]; - i.lastSeen = OSUtils::now(); - if (physicalAddress) { - i.physicalAddress = physicalAddress; - } - i.osArch = std::string(osArch); -} - -void CV1::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress) -{ - this->nodeIsOnline(networkId, memberId, physicalAddress, "unknown/unknown"); -} - -AuthInfo CV1::getSSOAuthInfo(const nlohmann::json& member, const std::string& redirectURL) -{ - Metrics::db_get_sso_info++; - // NONCE is just a random character string. no semantic meaning - // state = HMAC SHA384 of Nonce based on shared sso key - // - // need nonce timeout in database? make sure it's used within X time - // X is 5 minutes for now. Make configurable later? - // - // how do we tell when a nonce is used? if auth_expiration_time is set - std::string networkId = member["nwid"]; - std::string memberId = member["id"]; - - char authenticationURL[4096] = { 0 }; - AuthInfo info; - info.enabled = true; - - // if (memberId == "a10dccea52" && networkId == "8056c2e21c24673d") { - // fprintf(stderr, "invalid authinfo for grant's machine\n"); - // info.version=1; - // return info; - // } - // fprintf(stderr, "PostgreSQL::updateMemberOnLoad: %s-%s\n", networkId.c_str(), memberId.c_str()); - std::shared_ptr c; - try { - c = _pool->borrow(); - pqxx::work w(*c->c); - - char nonceBytes[16] = { 0 }; - std::string nonce = ""; - - // check if the member exists first. - pqxx::row count = w.exec_params1("SELECT count(id) FROM ztc_member WHERE id = $1 AND network_id = $2 AND deleted = false", memberId, networkId); - if (count[0].as() == 1) { - // get active nonce, if exists. - pqxx::result r = w.exec_params( - "SELECT nonce FROM ztc_sso_expiry " - "WHERE network_id = $1 AND member_id = $2 " - "AND ((NOW() AT TIME ZONE 'UTC') <= authentication_expiry_time) AND ((NOW() AT TIME ZONE 'UTC') <= nonce_expiration)", - networkId, - memberId); - - if (r.size() == 0) { - // no active nonce. - // find an unused nonce, if one exists. - pqxx::result r = w.exec_params( - "SELECT nonce FROM ztc_sso_expiry " - "WHERE network_id = $1 AND member_id = $2 " - "AND authentication_expiry_time IS NULL AND ((NOW() AT TIME ZONE 'UTC') <= nonce_expiration)", - networkId, - memberId); - - if (r.size() == 1) { - // we have an existing nonce. Use it - nonce = r.at(0)[0].as(); - Utils::unhex(nonce.c_str(), nonceBytes, sizeof(nonceBytes)); - } - else if (r.empty()) { - // create a nonce - Utils::getSecureRandom(nonceBytes, 16); - char nonceBuf[64] = { 0 }; - Utils::hex(nonceBytes, sizeof(nonceBytes), nonceBuf); - nonce = std::string(nonceBuf); - - pqxx::result ir = w.exec_params0( - "INSERT INTO ztc_sso_expiry " - "(nonce, nonce_expiration, network_id, member_id) VALUES " - "($1, TO_TIMESTAMP($2::double precision/1000), $3, $4)", - nonce, - OSUtils::now() + 300000, - networkId, - memberId); - - w.commit(); - } - else { - // > 1 ?!? Thats an error! - fprintf(stderr, "> 1 unused nonce!\n"); - exit(6); - } - } - else if (r.size() == 1) { - nonce = r.at(0)[0].as(); - Utils::unhex(nonce.c_str(), nonceBytes, sizeof(nonceBytes)); - } - else { - // more than 1 nonce in use? Uhhh... - fprintf(stderr, "> 1 nonce in use for network member?!?\n"); - exit(7); - } - - r = w.exec_params( - "SELECT oc.client_id, oc.authorization_endpoint, oc.issuer, oc.provider, oc.sso_impl_version " - "FROM ztc_network AS n " - "INNER JOIN ztc_org o " - " ON o.owner_id = n.owner_id " - "LEFT OUTER JOIN ztc_network_oidc_config noc " - " ON noc.network_id = n.id " - "LEFT OUTER JOIN ztc_oidc_config oc " - " ON noc.client_id = oc.client_id AND oc.org_id = o.org_id " - "WHERE n.id = $1 AND n.sso_enabled = true", - networkId); - - std::string client_id = ""; - std::string authorization_endpoint = ""; - std::string issuer = ""; - std::string provider = ""; - uint64_t sso_version = 0; - - if (r.size() == 1) { - client_id = r.at(0)[0].as >().value_or(""); - authorization_endpoint = r.at(0)[1].as >().value_or(""); - issuer = r.at(0)[2].as >().value_or(""); - provider = r.at(0)[3].as >().value_or(""); - sso_version = r.at(0)[4].as >().value_or(1); - } - else if (r.size() > 1) { - fprintf(stderr, "ERROR: More than one auth endpoint for an organization?!?!? NetworkID: %s\n", networkId.c_str()); - } - else { - fprintf(stderr, "No client or auth endpoint?!?\n"); - } - - info.version = sso_version; - - // no catch all else because we don't actually care if no records exist here. just continue as normal. - if ((! client_id.empty()) && (! authorization_endpoint.empty())) { - uint8_t state[48]; - HMACSHA384(_ssoPsk, nonceBytes, sizeof(nonceBytes), state); - char state_hex[256]; - Utils::hex(state, 48, state_hex); - - if (info.version == 0) { - char url[2048] = { 0 }; - OSUtils::ztsnprintf( - url, - sizeof(authenticationURL), - "%s?response_type=id_token&response_mode=form_post&scope=openid+email+profile&redirect_uri=%s&nonce=%s&state=%s&client_id=%s", - authorization_endpoint.c_str(), - url_encode(redirectURL).c_str(), - nonce.c_str(), - state_hex, - client_id.c_str()); - info.authenticationURL = std::string(url); - } - else if (info.version == 1) { - info.ssoClientID = client_id; - info.issuerURL = issuer; - info.ssoProvider = provider; - info.ssoNonce = nonce; - info.ssoState = std::string(state_hex) + "_" + networkId; - info.centralAuthURL = redirectURL; -#ifdef ZT_DEBUG - fprintf( - stderr, - "ssoClientID: %s\nissuerURL: %s\nssoNonce: %s\nssoState: %s\ncentralAuthURL: %s\nprovider: %s\n", - info.ssoClientID.c_str(), - info.issuerURL.c_str(), - info.ssoNonce.c_str(), - info.ssoState.c_str(), - info.centralAuthURL.c_str(), - provider.c_str()); -#endif - } - } - else { - fprintf(stderr, "client_id: %s\nauthorization_endpoint: %s\n", client_id.c_str(), authorization_endpoint.c_str()); - } - } - - _pool->unborrow(c); - } - catch (std::exception& e) { - fprintf(stderr, "ERROR: Error updating member on load for network %s: %s\n", networkId.c_str(), e.what()); - } - - return info; // std::string(authenticationURL); -} - -void CV1::initializeNetworks() -{ - try { - std::string setKey = "networks:{" + _myAddressStr + "}"; - - fprintf(stderr, "Initializing Networks...\n"); - - if (_redisMemberStatus) { - fprintf(stderr, "Init Redis for networks...\n"); - try { - if (_rc->clusterMode) { - _cluster->del(setKey); - } - else { - _redis->del(setKey); - } - } - catch (sw::redis::Error& e) { - // ignore. if this key doesn't exist, there's no reason to delete it - } - } - - std::unordered_set networkSet; - - char qbuf[2048] = { 0 }; - sprintf( - qbuf, - "SELECT n.id, (EXTRACT(EPOCH FROM n.creation_time AT TIME ZONE 'UTC')*1000)::bigint as creation_time, n.capabilities, " - "n.enable_broadcast, (EXTRACT(EPOCH FROM n.last_modified AT TIME ZONE 'UTC')*1000)::bigint AS last_modified, n.mtu, n.multicast_limit, n.name, n.private, n.remote_trace_level, " - "n.remote_trace_target, n.revision, n.rules, n.tags, n.v4_assign_mode, n.v6_assign_mode, n.sso_enabled, (CASE WHEN n.sso_enabled THEN noc.client_id ELSE NULL END) as client_id, " - "(CASE WHEN n.sso_enabled THEN oc.authorization_endpoint ELSE NULL END) as authorization_endpoint, " - "(CASE WHEN n.sso_enabled THEN oc.provider ELSE NULL END) as provider, d.domain, d.servers, " - "ARRAY(SELECT CONCAT(host(ip_range_start),'|', host(ip_range_end)) FROM ztc_network_assignment_pool WHERE network_id = n.id) AS assignment_pool, " - "ARRAY(SELECT CONCAT(host(address),'/',bits::text,'|',COALESCE(host(via), 'NULL'))FROM ztc_network_route WHERE network_id = n.id) AS routes " - "FROM ztc_network n " - "LEFT OUTER JOIN ztc_org o " - " ON o.owner_id = n.owner_id " - "LEFT OUTER JOIN ztc_network_oidc_config noc " - " ON noc.network_id = n.id " - "LEFT OUTER JOIN ztc_oidc_config oc " - " ON noc.client_id = oc.client_id AND oc.org_id = o.org_id " - "LEFT OUTER JOIN ztc_network_dns d " - " ON d.network_id = n.id " - "WHERE deleted = false AND controller_id = '%s'", - _myAddressStr.c_str()); - auto c = _pool->borrow(); - auto c2 = _pool->borrow(); - pqxx::work w { *c->c }; - - fprintf(stderr, "Load networks from psql...\n"); - auto stream = pqxx::stream_from::query(w, qbuf); - - std::tuple< - std::string // network ID - , - std::optional // creationTime - , - std::optional // capabilities - , - std::optional // enableBroadcast - , - std::optional // lastModified - , - std::optional // mtu - , - std::optional // multicastLimit - , - std::optional // name - , - bool // private - , - std::optional // remoteTraceLevel - , - std::optional // remoteTraceTarget - , - std::optional // revision - , - std::optional // rules - , - std::optional // tags - , - std::optional // v4AssignMode - , - std::optional // v6AssignMode - , - std::optional // ssoEnabled - , - std::optional // clientId - , - std::optional // authorizationEndpoint - , - std::optional // ssoProvider - , - std::optional // domain - , - std::optional // servers - , - std::string // assignmentPoolString - , - std::string // routeString - > - row; - - uint64_t count = 0; - auto tmp = std::chrono::high_resolution_clock::now(); - uint64_t total = 0; - while (stream >> row) { - auto start = std::chrono::high_resolution_clock::now(); - - json empty; - json config; - - initNetwork(config); - - std::string nwid = std::get<0>(row); - std::optional creationTime = std::get<1>(row); - std::optional capabilities = std::get<2>(row); - std::optional enableBroadcast = std::get<3>(row); - std::optional lastModified = std::get<4>(row); - std::optional mtu = std::get<5>(row); - std::optional multicastLimit = std::get<6>(row); - std::optional name = std::get<7>(row); - bool isPrivate = std::get<8>(row); - std::optional remoteTraceLevel = std::get<9>(row); - std::optional remoteTraceTarget = std::get<10>(row); - std::optional revision = std::get<11>(row); - std::optional rules = std::get<12>(row); - std::optional tags = std::get<13>(row); - std::optional v4AssignMode = std::get<14>(row); - std::optional v6AssignMode = std::get<15>(row); - std::optional ssoEnabled = std::get<16>(row); - std::optional clientId = std::get<17>(row); - std::optional authorizationEndpoint = std::get<18>(row); - std::optional ssoProvider = std::get<19>(row); - std::optional dnsDomain = std::get<20>(row); - std::optional dnsServers = std::get<21>(row); - std::string assignmentPoolString = std::get<22>(row); - std::string routesString = std::get<23>(row); - - config["id"] = nwid; - config["nwid"] = nwid; - config["creationTime"] = creationTime.value_or(0); - config["capabilities"] = json::parse(capabilities.value_or("[]")); - config["enableBroadcast"] = enableBroadcast.value_or(false); - config["lastModified"] = lastModified.value_or(0); - config["mtu"] = mtu.value_or(2800); - config["multicastLimit"] = multicastLimit.value_or(64); - config["name"] = name.value_or(""); - config["private"] = isPrivate; - config["remoteTraceLevel"] = remoteTraceLevel.value_or(0); - config["remoteTraceTarget"] = remoteTraceTarget.value_or(""); - config["revision"] = revision.value_or(0); - config["rules"] = json::parse(rules.value_or("[]")); - config["tags"] = json::parse(tags.value_or("[]")); - config["v4AssignMode"] = json::parse(v4AssignMode.value_or("{}")); - config["v6AssignMode"] = json::parse(v6AssignMode.value_or("{}")); - config["ssoEnabled"] = ssoEnabled.value_or(false); - config["objtype"] = "network"; - config["ipAssignmentPools"] = json::array(); - config["routes"] = json::array(); - config["clientId"] = clientId.value_or(""); - config["authorizationEndpoint"] = authorizationEndpoint.value_or(""); - config["provider"] = ssoProvider.value_or(""); - - networkSet.insert(nwid); - - if (dnsDomain.has_value()) { - std::string serverList = dnsServers.value(); - json obj; - auto servers = json::array(); - if (serverList.rfind("{", 0) != std::string::npos) { - serverList = serverList.substr(1, serverList.size() - 2); - std::stringstream ss(serverList); - while (ss.good()) { - std::string server; - std::getline(ss, server, ','); - servers.push_back(server); - } - } - obj["domain"] = dnsDomain.value(); - obj["servers"] = servers; - config["dns"] = obj; - } - - config["ipAssignmentPools"] = json::array(); - if (assignmentPoolString != "{}") { - std::string tmp = assignmentPoolString.substr(1, assignmentPoolString.size() - 2); - std::vector assignmentPools = split(tmp, ','); - for (auto it = assignmentPools.begin(); it != assignmentPools.end(); ++it) { - std::vector r = split(*it, '|'); - json ip; - ip["ipRangeStart"] = r[0]; - ip["ipRangeEnd"] = r[1]; - config["ipAssignmentPools"].push_back(ip); - } - } - - config["routes"] = json::array(); - if (routesString != "{}") { - std::string tmp = routesString.substr(1, routesString.size() - 2); - std::vector routes = split(tmp, ','); - for (auto it = routes.begin(); it != routes.end(); ++it) { - std::vector r = split(*it, '|'); - json route; - route["target"] = r[0]; - route["via"] = ((route["via"] == "NULL") ? nullptr : r[1]); - config["routes"].push_back(route); - } - } - - Metrics::network_count++; - - _networkChanged(empty, config, false); - - auto end = std::chrono::high_resolution_clock::now(); - auto dur = std::chrono::duration_cast(end - start); - ; - total += dur.count(); - ++count; - if (count > 0 && count % 10000 == 0) { - fprintf(stderr, "Averaging %llu us per network\n", (total / count)); - } - } - - if (count > 0) { - fprintf(stderr, "Took %llu us per network to load\n", (total / count)); - } - stream.complete(); - - w.commit(); - _pool->unborrow(c2); - _pool->unborrow(c); - fprintf(stderr, "done.\n"); - - if (! networkSet.empty()) { - if (_redisMemberStatus) { - fprintf(stderr, "adding networks to redis...\n"); - if (_rc->clusterMode) { - auto tx = _cluster->transaction(_myAddressStr, true, false); - uint64_t count = 0; - for (std::string nwid : networkSet) { - tx.sadd(setKey, nwid); - if (++count % 30000 == 0) { - tx.exec(); - tx = _cluster->transaction(_myAddressStr, true, false); - } - } - tx.exec(); - } - else { - auto tx = _redis->transaction(true, false); - uint64_t count = 0; - for (std::string nwid : networkSet) { - tx.sadd(setKey, nwid); - if (++count % 30000 == 0) { - tx.exec(); - tx = _redis->transaction(true, false); - } - } - tx.exec(); - } - fprintf(stderr, "done.\n"); - } - } - - if (++this->_ready == 2) { - if (_waitNoticePrinted) { - fprintf(stderr, "[%s] NOTICE: %.10llx controller PostgreSQL data download complete." ZT_EOL_S, _timestr(), (unsigned long long)_myAddress.toInt()); - } - _readyLock.unlock(); - } - fprintf(stderr, "network init done.\n"); - } - catch (sw::redis::Error& e) { - fprintf(stderr, "ERROR: Error initializing networks in Redis: %s\n", e.what()); - std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - exit(-1); - } - catch (std::exception& e) { - fprintf(stderr, "ERROR: Error initializing networks: %s\n", e.what()); - std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - exit(-1); - } -} - -void CV1::initializeMembers() -{ - std::string memberId; - std::string networkId; - try { - std::unordered_map networkMembers; - fprintf(stderr, "Initializing Members...\n"); - - std::string setKeyBase = "network-nodes-all:{" + _myAddressStr + "}:"; - - if (_redisMemberStatus) { - fprintf(stderr, "Initialize Redis for members...\n"); - std::unique_lock l(_networks_l); - std::unordered_set deletes; - for (auto it : _networks) { - uint64_t nwid_i = it.first; - char nwidTmp[64] = { 0 }; - OSUtils::ztsnprintf(nwidTmp, sizeof(nwidTmp), "%.16llx", nwid_i); - std::string nwid(nwidTmp); - std::string key = setKeyBase + nwid; - deletes.insert(key); - } - - if (! deletes.empty()) { - try { - if (_rc->clusterMode) { - auto tx = _cluster->transaction(_myAddressStr, true, false); - for (std::string k : deletes) { - tx.del(k); - } - tx.exec(); - } - else { - auto tx = _redis->transaction(true, false); - for (std::string k : deletes) { - tx.del(k); - } - tx.exec(); - } - } - catch (sw::redis::Error& e) { - // ignore - } - } - } - - char qbuf[2048]; - sprintf( - qbuf, - "SELECT m.id, m.network_id, m.active_bridge, m.authorized, m.capabilities, " - "(EXTRACT(EPOCH FROM m.creation_time AT TIME ZONE 'UTC')*1000)::bigint, m.identity, " - "(EXTRACT(EPOCH FROM m.last_authorized_time AT TIME ZONE 'UTC')*1000)::bigint, " - "(EXTRACT(EPOCH FROM m.last_deauthorized_time AT TIME ZONE 'UTC')*1000)::bigint, " - "m.remote_trace_level, m.remote_trace_target, m.tags, m.v_major, m.v_minor, m.v_rev, m.v_proto, " - "m.no_auto_assign_ips, m.revision, m.sso_exempt, " - "(CASE WHEN n.sso_enabled = TRUE AND m.sso_exempt = FALSE THEN " - " ( " - " SELECT (EXTRACT(EPOCH FROM e.authentication_expiry_time)*1000)::bigint " - " FROM ztc_sso_expiry e " - " INNER JOIN ztc_network n1 " - " ON n1.id = e.network_id AND n1.deleted = TRUE " - " WHERE e.network_id = m.network_id AND e.member_id = m.id AND n.sso_enabled = TRUE AND e.authentication_expiry_time IS NOT NULL " - " ORDER BY e.authentication_expiry_time DESC LIMIT 1 " - " ) " - " ELSE NULL " - " END) AS authentication_expiry_time, " - "ARRAY(SELECT DISTINCT address FROM ztc_member_ip_assignment WHERE member_id = m.id AND network_id = m.network_id) AS assigned_addresses " - "FROM ztc_member m " - "INNER JOIN ztc_network n " - " ON n.id = m.network_id " - "WHERE n.controller_id = '%s' AND n.deleted = FALSE AND m.deleted = FALSE", - _myAddressStr.c_str()); - auto c = _pool->borrow(); - auto c2 = _pool->borrow(); - pqxx::work w { *c->c }; - - fprintf(stderr, "Load members from psql...\n"); - auto stream = pqxx::stream_from::query(w, qbuf); - - std::tuple< - std::string // memberId - , - std::string // memberId - , - std::optional // activeBridge - , - std::optional // authorized - , - std::optional // capabilities - , - std::optional // creationTime - , - std::optional // identity - , - std::optional // lastAuthorizedTime - , - std::optional // lastDeauthorizedTime - , - std::optional // remoteTraceLevel - , - std::optional // remoteTraceTarget - , - std::optional // tags - , - std::optional // vMajor - , - std::optional // vMinor - , - std::optional // vRev - , - std::optional // vProto - , - std::optional // noAutoAssignIps - , - std::optional // revision - , - std::optional // ssoExempt - , - std::optional // authenticationExpiryTime - , - std::string // assignedAddresses - > - row; - - uint64_t count = 0; - auto tmp = std::chrono::high_resolution_clock::now(); - uint64_t total = 0; - while (stream >> row) { - auto start = std::chrono::high_resolution_clock::now(); - json empty; - json config; - - initMember(config); - - memberId = std::get<0>(row); - networkId = std::get<1>(row); - std::optional activeBridge = std::get<2>(row); - std::optional authorized = std::get<3>(row); - std::optional capabilities = std::get<4>(row); - std::optional creationTime = std::get<5>(row); - std::optional identity = std::get<6>(row); - std::optional lastAuthorizedTime = std::get<7>(row); - std::optional lastDeauthorizedTime = std::get<8>(row); - std::optional remoteTraceLevel = std::get<9>(row); - std::optional remoteTraceTarget = std::get<10>(row); - std::optional tags = std::get<11>(row); - std::optional vMajor = std::get<12>(row); - std::optional vMinor = std::get<13>(row); - std::optional vRev = std::get<14>(row); - std::optional vProto = std::get<15>(row); - std::optional noAutoAssignIps = std::get<16>(row); - std::optional revision = std::get<17>(row); - std::optional ssoExempt = std::get<18>(row); - std::optional authenticationExpiryTime = std::get<19>(row); - std::string assignedAddresses = std::get<20>(row); - - networkMembers.insert(std::pair(setKeyBase + networkId, memberId)); - - config["id"] = memberId; - config["address"] = memberId; - config["nwid"] = networkId; - config["activeBridge"] = activeBridge.value_or(false); - config["authorized"] = authorized.value_or(false); - config["capabilities"] = json::parse(capabilities.value_or("[]")); - config["creationTime"] = creationTime.value_or(0); - config["identity"] = identity.value_or(""); - config["lastAuthorizedTime"] = lastAuthorizedTime.value_or(0); - config["lastDeauthorizedTime"] = lastDeauthorizedTime.value_or(0); - config["remoteTraceLevel"] = remoteTraceLevel.value_or(0); - config["remoteTraceTarget"] = remoteTraceTarget.value_or(""); - config["tags"] = json::parse(tags.value_or("[]")); - config["vMajor"] = vMajor.value_or(-1); - config["vMinor"] = vMinor.value_or(-1); - config["vRev"] = vRev.value_or(-1); - config["vProto"] = vProto.value_or(-1); - config["noAutoAssignIps"] = noAutoAssignIps.value_or(false); - config["revision"] = revision.value_or(0); - config["ssoExempt"] = ssoExempt.value_or(false); - config["authenticationExpiryTime"] = authenticationExpiryTime.value_or(0); - config["objtype"] = "member"; - config["ipAssignments"] = json::array(); - - if (assignedAddresses != "{}") { - std::string tmp = assignedAddresses.substr(1, assignedAddresses.size() - 2); - std::vector addrs = split(tmp, ','); - for (auto it = addrs.begin(); it != addrs.end(); ++it) { - config["ipAssignments"].push_back(*it); - } - } - - Metrics::member_count++; - - _memberChanged(empty, config, false); - - memberId = ""; - networkId = ""; - - auto end = std::chrono::high_resolution_clock::now(); - auto dur = std::chrono::duration_cast(end - start); - total += dur.count(); - ++count; - if (count > 0 && count % 10000 == 0) { - fprintf(stderr, "Averaging %llu us per member\n", (total / count)); - } - } - if (count > 0) { - fprintf(stderr, "Took %llu us per member to load\n", (total / count)); - } - - stream.complete(); - - w.commit(); - _pool->unborrow(c2); - _pool->unborrow(c); - fprintf(stderr, "done.\n"); - - if (! networkMembers.empty()) { - if (_redisMemberStatus) { - fprintf(stderr, "Load member data into redis...\n"); - if (_rc->clusterMode) { - auto tx = _cluster->transaction(_myAddressStr, true, false); - uint64_t count = 0; - for (auto it : networkMembers) { - tx.sadd(it.first, it.second); - if (++count % 30000 == 0) { - tx.exec(); - tx = _cluster->transaction(_myAddressStr, true, false); - } - } - tx.exec(); - } - else { - auto tx = _redis->transaction(true, false); - uint64_t count = 0; - for (auto it : networkMembers) { - tx.sadd(it.first, it.second); - if (++count % 30000 == 0) { - tx.exec(); - tx = _redis->transaction(true, false); - } - } - tx.exec(); - } - fprintf(stderr, "done.\n"); - } - } - - fprintf(stderr, "Done loading members...\n"); - - if (++this->_ready == 2) { - if (_waitNoticePrinted) { - fprintf(stderr, "[%s] NOTICE: %.10llx controller PostgreSQL data download complete." ZT_EOL_S, _timestr(), (unsigned long long)_myAddress.toInt()); - } - _readyLock.unlock(); - } - } - catch (sw::redis::Error& e) { - fprintf(stderr, "ERROR: Error initializing members (redis): %s\n", e.what()); - exit(-1); - } - catch (std::exception& e) { - fprintf(stderr, "ERROR: Error initializing member: %s-%s %s\n", networkId.c_str(), memberId.c_str(), e.what()); - exit(-1); - } -} - -void CV1::heartbeat() -{ - char publicId[1024]; - char hostnameTmp[1024]; - _myId.toString(false, publicId); - if (gethostname(hostnameTmp, sizeof(hostnameTmp)) != 0) { - hostnameTmp[0] = (char)0; - } - else { - for (int i = 0; i < (int)sizeof(hostnameTmp); ++i) { - if ((hostnameTmp[i] == '.') || (hostnameTmp[i] == 0)) { - hostnameTmp[i] = (char)0; - break; - } - } - } - const char* controllerId = _myAddressStr.c_str(); - const char* publicIdentity = publicId; - const char* hostname = hostnameTmp; - - while (_run == 1) { - // fprintf(stderr, "%s: heartbeat\n", controllerId); - auto c = _pool->borrow(); - int64_t ts = OSUtils::now(); - - if (c->c) { - std::string major = std::to_string(ZEROTIER_ONE_VERSION_MAJOR); - std::string minor = std::to_string(ZEROTIER_ONE_VERSION_MINOR); - std::string rev = std::to_string(ZEROTIER_ONE_VERSION_REVISION); - std::string build = std::to_string(ZEROTIER_ONE_VERSION_BUILD); - std::string now = std::to_string(ts); - std::string host_port = std::to_string(_listenPort); - std::string use_redis = (_rc != NULL) ? "true" : "false"; - std::string redis_mem_status = (_redisMemberStatus) ? "true" : "false"; - - try { - pqxx::work w { *c->c }; - - pqxx::result res = w.exec0( - "INSERT INTO ztc_controller (id, cluster_host, last_alive, public_identity, v_major, v_minor, v_rev, v_build, host_port, use_redis, redis_member_status) " - "VALUES (" - + w.quote(controllerId) + ", " + w.quote(hostname) + ", TO_TIMESTAMP(" + now + "::double precision/1000), " + w.quote(publicIdentity) + ", " + major + ", " + minor + ", " + rev + ", " + build + ", " + host_port + ", " - + use_redis + ", " + redis_mem_status - + ") " - "ON CONFLICT (id) DO UPDATE SET cluster_host = EXCLUDED.cluster_host, last_alive = EXCLUDED.last_alive, " - "public_identity = EXCLUDED.public_identity, v_major = EXCLUDED.v_major, v_minor = EXCLUDED.v_minor, " - "v_rev = EXCLUDED.v_rev, v_build = EXCLUDED.v_rev, host_port = EXCLUDED.host_port, " - "use_redis = EXCLUDED.use_redis, redis_member_status = EXCLUDED.redis_member_status"); - w.commit(); - } - catch (std::exception& e) { - fprintf(stderr, "%s: Heartbeat update failed: %s\n", controllerId, e.what()); - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - continue; - } - } - _pool->unborrow(c); - - try { - if (_redisMemberStatus) { - if (_rc->clusterMode) { - _cluster->zadd("controllers", "controllerId", ts); - } - else { - _redis->zadd("controllers", "controllerId", ts); - } - } - } - catch (sw::redis::Error& e) { - fprintf(stderr, "ERROR: Redis error in heartbeat thread: %s\n", e.what()); - } - - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - } - fprintf(stderr, "Exited heartbeat thread\n"); -} - -void CV1::membersDbWatcher() -{ - if (_rc) { - _membersWatcher_Redis(); - } - else { - _membersWatcher_Postgres(); - } - - if (_run == 1) { - fprintf(stderr, "ERROR: %s membersDbWatcher should still be running! Exiting Controller.\n", _myAddressStr.c_str()); - exit(9); - } - fprintf(stderr, "Exited membersDbWatcher\n"); -} - -void CV1::_membersWatcher_Postgres() -{ - auto c = _pool->borrow(); - - std::string stream = "member_" + _myAddressStr; - - fprintf(stderr, "Listening to member stream: %s\n", stream.c_str()); - MemberNotificationReceiver m(this, *c->c, stream); - - while (_run == 1) { - c->c->await_notification(5, 0); - } - - _pool->unborrow(c); -} - -void CV1::_membersWatcher_Redis() -{ - char buf[11] = { 0 }; - std::string key = "member-stream:{" + std::string(_myAddress.toString(buf)) + "}"; - std::string lastID = "0"; - fprintf(stderr, "Listening to member stream: %s\n", key.c_str()); - while (_run == 1) { - try { - json tmp; - std::unordered_map result; - if (_rc->clusterMode) { - _cluster->xread(key, lastID, std::chrono::seconds(1), 0, std::inserter(result, result.end())); - } - else { - _redis->xread(key, lastID, std::chrono::seconds(1), 0, std::inserter(result, result.end())); - } - if (! result.empty()) { - for (auto element : result) { -#ifdef REDIS_TRACE - fprintf(stdout, "Received notification from: %s\n", element.first.c_str()); -#endif - for (auto rec : element.second) { - std::string id = rec.first; - auto attrs = rec.second; -#ifdef REDIS_TRACE - fprintf(stdout, "Record ID: %s\n", id.c_str()); - fprintf(stdout, "attrs len: %lu\n", attrs.size()); -#endif - for (auto a : attrs) { -#ifdef REDIS_TRACE - fprintf(stdout, "key: %s\nvalue: %s\n", a.first.c_str(), a.second.c_str()); -#endif - try { - tmp = json::parse(a.second); - json& ov = tmp["old_val"]; - json& nv = tmp["new_val"]; - json oldConfig, newConfig; - if (ov.is_object()) - oldConfig = ov; - if (nv.is_object()) - newConfig = nv; - if (oldConfig.is_object() || newConfig.is_object()) { - _memberChanged(oldConfig, newConfig, (this->_ready >= 2)); - } - } - catch (...) { - fprintf(stderr, "json parse error in _membersWatcher_Redis: %s\n", a.second.c_str()); - } - } - if (_rc->clusterMode) { - _cluster->xdel(key, id); - } - else { - _redis->xdel(key, id); - } - lastID = id; - Metrics::redis_mem_notification++; - } - } - } - } - catch (sw::redis::Error& e) { - fprintf(stderr, "Error in Redis members watcher: %s\n", e.what()); - } - } - fprintf(stderr, "membersWatcher ended\n"); -} - -void CV1::networksDbWatcher() -{ - if (_rc) { - _networksWatcher_Redis(); - } - else { - _networksWatcher_Postgres(); - } - - if (_run == 1) { - fprintf(stderr, "ERROR: %s networksDbWatcher should still be running! Exiting Controller.\n", _myAddressStr.c_str()); - exit(8); - } - fprintf(stderr, "Exited networksDbWatcher\n"); -} - -void CV1::_networksWatcher_Postgres() -{ - std::string stream = "network_" + _myAddressStr; - - fprintf(stderr, "Listening to member stream: %s\n", stream.c_str()); - - auto c = _pool->borrow(); - - NetworkNotificationReceiver n(this, *c->c, stream); - - while (_run == 1) { - c->c->await_notification(5, 0); - } -} - -void CV1::_networksWatcher_Redis() -{ - char buf[11] = { 0 }; - std::string key = "network-stream:{" + std::string(_myAddress.toString(buf)) + "}"; - std::string lastID = "0"; - while (_run == 1) { - try { - json tmp; - std::unordered_map result; - if (_rc->clusterMode) { - _cluster->xread(key, lastID, std::chrono::seconds(1), 0, std::inserter(result, result.end())); - } - else { - _redis->xread(key, lastID, std::chrono::seconds(1), 0, std::inserter(result, result.end())); - } - - if (! result.empty()) { - for (auto element : result) { -#ifdef REDIS_TRACE - fprintf(stdout, "Received notification from: %s\n", element.first.c_str()); -#endif - for (auto rec : element.second) { - std::string id = rec.first; - auto attrs = rec.second; -#ifdef REDIS_TRACE - fprintf(stdout, "Record ID: %s\n", id.c_str()); - fprintf(stdout, "attrs len: %lu\n", attrs.size()); -#endif - for (auto a : attrs) { -#ifdef REDIS_TRACE - fprintf(stdout, "key: %s\nvalue: %s\n", a.first.c_str(), a.second.c_str()); -#endif - try { - tmp = json::parse(a.second); - json& ov = tmp["old_val"]; - json& nv = tmp["new_val"]; - json oldConfig, newConfig; - if (ov.is_object()) - oldConfig = ov; - if (nv.is_object()) - newConfig = nv; - if (oldConfig.is_object() || newConfig.is_object()) { - _networkChanged(oldConfig, newConfig, (this->_ready >= 2)); - } - } - catch (std::exception& e) { - fprintf(stderr, "json parse error in networkWatcher_Redis: what: %s json: %s\n", e.what(), a.second.c_str()); - } - } - if (_rc->clusterMode) { - _cluster->xdel(key, id); - } - else { - _redis->xdel(key, id); - } - lastID = id; - } - Metrics::redis_net_notification++; - } - } - } - catch (sw::redis::Error& e) { - fprintf(stderr, "Error in Redis networks watcher: %s\n", e.what()); - } - } - fprintf(stderr, "networksWatcher ended\n"); -} - -void CV1::commitThread() -{ - fprintf(stderr, "%s: commitThread start\n", _myAddressStr.c_str()); - std::pair qitem; - while (_commitQueue.get(qitem) & (_run == 1)) { - // fprintf(stderr, "commitThread tick\n"); - if (! qitem.first.is_object()) { - fprintf(stderr, "not an object\n"); - continue; - } - - std::shared_ptr c; - try { - c = _pool->borrow(); - } - catch (std::exception& e) { - fprintf(stderr, "ERROR: %s\n", e.what()); - continue; - } - - if (! c) { - fprintf(stderr, "Error getting database connection\n"); - continue; - } - - Metrics::pgsql_commit_ticks++; - try { - nlohmann::json& config = (qitem.first); - const std::string objtype = config["objtype"]; - if (objtype == "member") { - // fprintf(stderr, "%s: commitThread: member\n", _myAddressStr.c_str()); - std::string memberId; - std::string networkId; - try { - pqxx::work w(*c->c); - - memberId = config["id"]; - networkId = config["nwid"]; - - std::string target = "NULL"; - if (! config["remoteTraceTarget"].is_null()) { - target = config["remoteTraceTarget"]; - } - - pqxx::row nwrow = w.exec_params1("SELECT COUNT(id) FROM ztc_network WHERE id = $1", networkId); - int nwcount = nwrow[0].as(); - - if (nwcount != 1) { - fprintf(stderr, "network %s does not exist. skipping member upsert\n", networkId.c_str()); - w.abort(); - _pool->unborrow(c); - continue; - } - - pqxx::row mrow = w.exec_params1("SELECT COUNT(id) FROM ztc_member WHERE id = $1 AND network_id = $2", memberId, networkId); - int membercount = mrow[0].as(); - - bool isNewMember = false; - if (membercount == 0) { - // new member - isNewMember = true; - pqxx::result res = w.exec_params0( - "INSERT INTO ztc_member (id, network_id, active_bridge, authorized, capabilities, " - "identity, last_authorized_time, last_deauthorized_time, no_auto_assign_ips, " - "remote_trace_level, remote_trace_target, revision, tags, v_major, v_minor, v_rev, v_proto) " - "VALUES ($1, $2, $3, $4, $5, $6, " - "TO_TIMESTAMP($7::double precision/1000), TO_TIMESTAMP($8::double precision/1000), " - "$9, $10, $11, $12, $13, $14, $15, $16, $17)", - memberId, - networkId, - (bool)config["activeBridge"], - (bool)config["authorized"], - OSUtils::jsonDump(config["capabilities"], -1), - OSUtils::jsonString(config["identity"], ""), - (uint64_t)config["lastAuthorizedTime"], - (uint64_t)config["lastDeauthorizedTime"], - (bool)config["noAutoAssignIps"], - (int)config["remoteTraceLevel"], - target, - (uint64_t)config["revision"], - OSUtils::jsonDump(config["tags"], -1), - (int)config["vMajor"], - (int)config["vMinor"], - (int)config["vRev"], - (int)config["vProto"]); - } - else { - // existing member - pqxx::result res = w.exec_params0( - "UPDATE ztc_member " - "SET active_bridge = $3, authorized = $4, capabilities = $5, identity = $6, " - "last_authorized_time = TO_TIMESTAMP($7::double precision/1000), " - "last_deauthorized_time = TO_TIMESTAMP($8::double precision/1000), " - "no_auto_assign_ips = $9, remote_trace_level = $10, remote_trace_target= $11, " - "revision = $12, tags = $13, v_major = $14, v_minor = $15, v_rev = $16, v_proto = $17 " - "WHERE id = $1 AND network_id = $2", - memberId, - networkId, - (bool)config["activeBridge"], - (bool)config["authorized"], - OSUtils::jsonDump(config["capabilities"], -1), - OSUtils::jsonString(config["identity"], ""), - (uint64_t)config["lastAuthorizedTime"], - (uint64_t)config["lastDeauthorizedTime"], - (bool)config["noAutoAssignIps"], - (int)config["remoteTraceLevel"], - target, - (uint64_t)config["revision"], - OSUtils::jsonDump(config["tags"], -1), - (int)config["vMajor"], - (int)config["vMinor"], - (int)config["vRev"], - (int)config["vProto"]); - } - - if (! isNewMember) { - pqxx::result res = w.exec_params0("DELETE FROM ztc_member_ip_assignment WHERE member_id = $1 AND network_id = $2", memberId, networkId); - } - - std::vector assignments; - bool ipAssignError = false; - for (auto i = config["ipAssignments"].begin(); i != config["ipAssignments"].end(); ++i) { - std::string addr = *i; - - if (std::find(assignments.begin(), assignments.end(), addr) != assignments.end()) { - continue; - } - - pqxx::result res = w.exec_params0("INSERT INTO ztc_member_ip_assignment (member_id, network_id, address) VALUES ($1, $2, $3) ON CONFLICT (network_id, member_id, address) DO NOTHING", memberId, networkId, addr); - - assignments.push_back(addr); - } - if (ipAssignError) { - fprintf(stderr, "%s: ipAssignError\n", _myAddressStr.c_str()); - w.abort(); - _pool->unborrow(c); - c.reset(); - continue; - } - - w.commit(); - - if (_smee != NULL && isNewMember) { - pqxx::row row = w.exec_params1( - "SELECT " - " count(h.hook_id) " - "FROM " - " ztc_hook h " - " INNER JOIN ztc_org o ON o.org_id = h.org_id " - " INNER JOIN ztc_network n ON n.owner_id = o.owner_id " - " WHERE " - "n.id = $1 ", - networkId); - int64_t hookCount = row[0].as(); - if (hookCount > 0) { - notifyNewMember(networkId, memberId); - } - } - - const uint64_t nwidInt = OSUtils::jsonIntHex(config["nwid"], 0ULL); - const uint64_t memberidInt = OSUtils::jsonIntHex(config["id"], 0ULL); - if (nwidInt && memberidInt) { - nlohmann::json nwOrig; - nlohmann::json memOrig; - - nlohmann::json memNew(config); - - get(nwidInt, nwOrig, memberidInt, memOrig); - - _memberChanged(memOrig, memNew, qitem.second); - } - else { - fprintf(stderr, "%s: Can't notify of change. Error parsing nwid or memberid: %llu-%llu\n", _myAddressStr.c_str(), (unsigned long long)nwidInt, (unsigned long long)memberidInt); - } - } - catch (std::exception& e) { - fprintf(stderr, "%s ERROR: Error updating member %s-%s: %s\n", _myAddressStr.c_str(), networkId.c_str(), memberId.c_str(), e.what()); - } - } - else if (objtype == "network") { - try { - // fprintf(stderr, "%s: commitThread: network\n", _myAddressStr.c_str()); - pqxx::work w(*c->c); - - std::string id = config["id"]; - std::string remoteTraceTarget = ""; - if (! config["remoteTraceTarget"].is_null()) { - remoteTraceTarget = config["remoteTraceTarget"]; - } - std::string rulesSource = ""; - if (config["rulesSource"].is_string()) { - rulesSource = config["rulesSource"]; - } - - // This ugly query exists because when we want to mirror networks to/from - // another data store (e.g. FileDB or LFDB) it is possible to get a network - // that doesn't exist in Central's database. This does an upsert and sets - // the owner_id to the "first" global admin in the user DB if the record - // did not previously exist. If the record already exists owner_id is left - // unchanged, so owner_id should be left out of the update clause. - pqxx::result res = w.exec_params0( - "INSERT INTO ztc_network (id, creation_time, owner_id, controller_id, capabilities, enable_broadcast, " - "last_modified, mtu, multicast_limit, name, private, " - "remote_trace_level, remote_trace_target, rules, rules_source, " - "tags, v4_assign_mode, v6_assign_mode, sso_enabled) VALUES (" - "$1, TO_TIMESTAMP($5::double precision/1000), " - "(SELECT user_id AS owner_id FROM ztc_global_permissions WHERE authorize = true AND del = true AND modify = true AND read = true LIMIT 1)," - "$2, $3, $4, TO_TIMESTAMP($5::double precision/1000), " - "$6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) " - "ON CONFLICT (id) DO UPDATE set controller_id = EXCLUDED.controller_id, " - "capabilities = EXCLUDED.capabilities, enable_broadcast = EXCLUDED.enable_broadcast, " - "last_modified = EXCLUDED.last_modified, mtu = EXCLUDED.mtu, " - "multicast_limit = EXCLUDED.multicast_limit, name = EXCLUDED.name, " - "private = EXCLUDED.private, remote_trace_level = EXCLUDED.remote_trace_level, " - "remote_trace_target = EXCLUDED.remote_trace_target, rules = EXCLUDED.rules, " - "rules_source = EXCLUDED.rules_source, tags = EXCLUDED.tags, " - "v4_assign_mode = EXCLUDED.v4_assign_mode, v6_assign_mode = EXCLUDED.v6_assign_mode, " - "sso_enabled = EXCLUDED.sso_enabled", - id, - _myAddressStr, - OSUtils::jsonDump(config["capabilities"], -1), - (bool)config["enableBroadcast"], - OSUtils::now(), - (int)config["mtu"], - (int)config["multicastLimit"], - OSUtils::jsonString(config["name"], ""), - (bool)config["private"], - (int)config["remoteTraceLevel"], - remoteTraceTarget, - OSUtils::jsonDump(config["rules"], -1), - rulesSource, - OSUtils::jsonDump(config["tags"], -1), - OSUtils::jsonDump(config["v4AssignMode"], -1), - OSUtils::jsonDump(config["v6AssignMode"], -1), - OSUtils::jsonBool(config["ssoEnabled"], false)); - - res = w.exec_params0("DELETE FROM ztc_network_assignment_pool WHERE network_id = $1", 0); - - auto pool = config["ipAssignmentPools"]; - bool err = false; - for (auto i = pool.begin(); i != pool.end(); ++i) { - std::string start = (*i)["ipRangeStart"]; - std::string end = (*i)["ipRangeEnd"]; - - res = w.exec_params0( - "INSERT INTO ztc_network_assignment_pool (network_id, ip_range_start, ip_range_end) " - "VALUES ($1, $2, $3)", - id, - start, - end); - } - - res = w.exec_params0("DELETE FROM ztc_network_route WHERE network_id = $1", id); - - auto routes = config["routes"]; - err = false; - for (auto i = routes.begin(); i != routes.end(); ++i) { - std::string t = (*i)["target"]; - std::vector target; - std::istringstream f(t); - std::string s; - while (std::getline(f, s, '/')) { - target.push_back(s); - } - if (target.empty() || target.size() != 2) { - continue; - } - std::string targetAddr = target[0]; - std::string targetBits = target[1]; - std::string via = "NULL"; - if (! (*i)["via"].is_null()) { - via = (*i)["via"]; - } - - res = w.exec_params0("INSERT INTO ztc_network_route (network_id, address, bits, via) VALUES ($1, $2, $3, $4)", id, targetAddr, targetBits, (via == "NULL" ? NULL : via.c_str())); - } - if (err) { - fprintf(stderr, "%s: route add error\n", _myAddressStr.c_str()); - w.abort(); - _pool->unborrow(c); - continue; - } - - auto dns = config["dns"]; - std::string domain = dns["domain"]; - std::stringstream servers; - servers << "{"; - for (auto j = dns["servers"].begin(); j < dns["servers"].end(); ++j) { - servers << *j; - if ((j + 1) != dns["servers"].end()) { - servers << ","; - } - } - servers << "}"; - - std::string s = servers.str(); - - res = w.exec_params0("INSERT INTO ztc_network_dns (network_id, domain, servers) VALUES ($1, $2, $3) ON CONFLICT (network_id) DO UPDATE SET domain = EXCLUDED.domain, servers = EXCLUDED.servers", id, domain, s); - - w.commit(); - - const uint64_t nwidInt = OSUtils::jsonIntHex(config["nwid"], 0ULL); - if (nwidInt) { - nlohmann::json nwOrig; - nlohmann::json nwNew(config); - - get(nwidInt, nwOrig); - - _networkChanged(nwOrig, nwNew, qitem.second); - } - else { - fprintf(stderr, "%s: Can't notify network changed: %llu\n", _myAddressStr.c_str(), (unsigned long long)nwidInt); - } - } - catch (std::exception& e) { - fprintf(stderr, "%s ERROR: Error updating network: %s\n", _myAddressStr.c_str(), e.what()); - } - if (_redisMemberStatus) { - try { - std::string id = config["id"]; - std::string controllerId = _myAddressStr.c_str(); - std::string key = "networks:{" + controllerId + "}"; - if (_rc->clusterMode) { - _cluster->sadd(key, id); - } - else { - _redis->sadd(key, id); - } - } - catch (sw::redis::Error& e) { - fprintf(stderr, "ERROR: Error adding network to Redis: %s\n", e.what()); - } - } - } - else if (objtype == "_delete_network") { - // fprintf(stderr, "%s: commitThread: delete network\n", _myAddressStr.c_str()); - try { - pqxx::work w(*c->c); - - std::string networkId = config["nwid"]; - - pqxx::result res = w.exec_params0("UPDATE ztc_network SET deleted = true WHERE id = $1", networkId); - - w.commit(); - } - catch (std::exception& e) { - fprintf(stderr, "%s ERROR: Error deleting network: %s\n", _myAddressStr.c_str(), e.what()); - } - if (_redisMemberStatus) { - try { - std::string id = config["id"]; - std::string controllerId = _myAddressStr.c_str(); - std::string key = "networks:{" + controllerId + "}"; - if (_rc->clusterMode) { - _cluster->srem(key, id); - _cluster->del("network-nodes-online:{" + controllerId + "}:" + id); - } - else { - _redis->srem(key, id); - _redis->del("network-nodes-online:{" + controllerId + "}:" + id); - } - } - catch (sw::redis::Error& e) { - fprintf(stderr, "ERROR: Error adding network to Redis: %s\n", e.what()); - } - } - } - else if (objtype == "_delete_member") { - // fprintf(stderr, "%s commitThread: delete member\n", _myAddressStr.c_str()); - try { - pqxx::work w(*c->c); - - std::string memberId = config["id"]; - std::string networkId = config["nwid"]; - - pqxx::result res = w.exec_params0("UPDATE ztc_member SET hidden = true, deleted = true WHERE id = $1 AND network_id = $2", memberId, networkId); - - w.commit(); - } - catch (std::exception& e) { - fprintf(stderr, "%s ERROR: Error deleting member: %s\n", _myAddressStr.c_str(), e.what()); - } - if (_redisMemberStatus) { - try { - std::string memberId = config["id"]; - std::string networkId = config["nwid"]; - std::string controllerId = _myAddressStr.c_str(); - std::string key = "network-nodes-all:{" + controllerId + "}:" + networkId; - if (_rc->clusterMode) { - _cluster->srem(key, memberId); - _cluster->del("member:{" + controllerId + "}:" + networkId + ":" + memberId); - } - else { - _redis->srem(key, memberId); - _redis->del("member:{" + controllerId + "}:" + networkId + ":" + memberId); - } - } - catch (sw::redis::Error& e) { - fprintf(stderr, "ERROR: Error deleting member from Redis: %s\n", e.what()); - } - } - } - else { - fprintf(stderr, "%s ERROR: unknown objtype\n", _myAddressStr.c_str()); - } - } - catch (std::exception& e) { - fprintf(stderr, "%s ERROR: Error getting objtype: %s\n", _myAddressStr.c_str(), e.what()); - } - _pool->unborrow(c); - c.reset(); - } - - fprintf(stderr, "%s commitThread finished\n", _myAddressStr.c_str()); -} - -void CV1::notifyNewMember(const std::string& networkID, const std::string& memberID) -{ - smeeclient::smee_client_notify_network_joined(_smee, networkID.c_str(), memberID.c_str()); -} - -void CV1::onlineNotificationThread() -{ - waitForReady(); - if (_redisMemberStatus) { - onlineNotification_Redis(); - } - else { - onlineNotification_Postgres(); - } -} - -/** - * ONLY UNCOMMENT FOR TEMPORARY DB MAINTENANCE - * - * This define temporarily turns off writing to the member status table - * so it can be reindexed when the indexes get too large. - */ - -// #define DISABLE_MEMBER_STATUS 1 - -void CV1::onlineNotification_Postgres() -{ - _connected = 1; - - nlohmann::json jtmp1, jtmp2; - while (_run == 1) { - auto c = _pool->borrow(); - auto c2 = _pool->borrow(); - try { - fprintf(stderr, "%s onlineNotification_Postgres\n", _myAddressStr.c_str()); - std::unordered_map, NodeOnlineRecord, _PairHasher> lastOnline; - { - std::lock_guard l(_lastOnline_l); - lastOnline.swap(_lastOnline); - } - -#ifndef DISABLE_MEMBER_STATUS - pqxx::work w(*c->c); - pqxx::work w2(*c2->c); - - fprintf(stderr, "online notification tick\n"); - - bool firstRun = true; - bool memberAdded = false; - int updateCount = 0; - - pqxx::pipeline pipe(w); - - for (auto i = lastOnline.begin(); i != lastOnline.end(); ++i) { - updateCount += 1; - uint64_t nwid_i = i->first.first; - char nwidTmp[64]; - char memTmp[64]; - char ipTmp[64]; - OSUtils::ztsnprintf(nwidTmp, sizeof(nwidTmp), "%.16llx", nwid_i); - OSUtils::ztsnprintf(memTmp, sizeof(memTmp), "%.10llx", i->first.second); - - if (! get(nwid_i, jtmp1, i->first.second, jtmp2)) { - continue; // skip non existent networks/members - } - - std::string networkId(nwidTmp); - std::string memberId(memTmp); - - try { - pqxx::row r = w2.exec_params1("SELECT id, network_id FROM ztc_member WHERE network_id = $1 AND id = $2", networkId, memberId); - } - catch (pqxx::unexpected_rows& e) { - continue; - } - - int64_t ts = i->second.lastSeen; - std::string ipAddr = i->second.physicalAddress.toIpString(ipTmp); - std::string timestamp = std::to_string(ts); - std::string osArch = i->second.osArch; - - std::stringstream memberUpdate; - memberUpdate << "INSERT INTO ztc_member_status (network_id, member_id, address, last_updated) VALUES " - << "('" << networkId << "', '" << memberId << "', "; - if (ipAddr.empty()) { - memberUpdate << "NULL, "; - } - else { - memberUpdate << "'" << ipAddr << "', "; - } - memberUpdate << "TO_TIMESTAMP(" << timestamp << "::double precision/1000)) " - << " ON CONFLICT (network_id, member_id) DO UPDATE SET address = EXCLUDED.address, last_updated = EXCLUDED.last_updated"; - - pipe.insert(memberUpdate.str()); - Metrics::pgsql_node_checkin++; - } - while (! pipe.empty()) { - pipe.retrieve(); - } - - pipe.complete(); - w.commit(); - fprintf(stderr, "%s: Updated online status of %d members\n", _myAddressStr.c_str(), updateCount); -#endif - } - catch (std::exception& e) { - fprintf(stderr, "%s: error in onlinenotification thread: %s\n", _myAddressStr.c_str(), e.what()); - } - _pool->unborrow(c2); - _pool->unborrow(c); - - ConnectionPoolStats stats = _pool->get_stats(); - fprintf(stderr, "%s pool stats: in use size: %llu, available size: %llu, total: %llu\n", _myAddressStr.c_str(), stats.borrowed_size, stats.pool_size, (stats.borrowed_size + stats.pool_size)); - - std::this_thread::sleep_for(std::chrono::seconds(10)); - } - fprintf(stderr, "%s: Fell out of run loop in onlineNotificationThread\n", _myAddressStr.c_str()); - if (_run == 1) { - fprintf(stderr, "ERROR: %s onlineNotificationThread should still be running! Exiting Controller.\n", _myAddressStr.c_str()); - exit(6); - } -} - -void CV1::onlineNotification_Redis() -{ - _connected = 1; - - char buf[11] = { 0 }; - std::string controllerId = std::string(_myAddress.toString(buf)); - - while (_run == 1) { - fprintf(stderr, "onlineNotification tick\n"); - auto start = std::chrono::high_resolution_clock::now(); - uint64_t count = 0; - - std::unordered_map, NodeOnlineRecord, _PairHasher> lastOnline; - { - std::lock_guard l(_lastOnline_l); - lastOnline.swap(_lastOnline); - } - try { - if (! lastOnline.empty()) { - if (_rc->clusterMode) { - auto tx = _cluster->transaction(controllerId, true, false); - count = _doRedisUpdate(tx, controllerId, lastOnline); - } - else { - auto tx = _redis->transaction(true, false); - count = _doRedisUpdate(tx, controllerId, lastOnline); - } - } - } - catch (sw::redis::Error& e) { - fprintf(stderr, "Error in online notification thread (redis): %s\n", e.what()); - } - - auto end = std::chrono::high_resolution_clock::now(); - auto dur = std::chrono::duration_cast(end - start); - auto total = dur.count(); - - fprintf(stderr, "onlineNotification ran in %llu ms\n", total); - - std::this_thread::sleep_for(std::chrono::seconds(5)); - } -} - -uint64_t CV1::_doRedisUpdate(sw::redis::Transaction& tx, std::string& controllerId, std::unordered_map, NodeOnlineRecord, _PairHasher>& lastOnline) - -{ - nlohmann::json jtmp1, jtmp2; - uint64_t count = 0; - for (auto i = lastOnline.begin(); i != lastOnline.end(); ++i) { - uint64_t nwid_i = i->first.first; - uint64_t memberid_i = i->first.second; - char nwidTmp[64]; - char memTmp[64]; - char ipTmp[64]; - OSUtils::ztsnprintf(nwidTmp, sizeof(nwidTmp), "%.16llx", nwid_i); - OSUtils::ztsnprintf(memTmp, sizeof(memTmp), "%.10llx", memberid_i); - - if (! get(nwid_i, jtmp1, memberid_i, jtmp2)) { - continue; // skip non existent members/networks - } - - std::string networkId(nwidTmp); - std::string memberId(memTmp); - - int64_t ts = i->second.lastSeen; - std::string ipAddr = i->second.physicalAddress.toIpString(ipTmp); - std::string timestamp = std::to_string(ts); - std::string osArch = i->second.osArch; - - std::unordered_map record = { { "id", memberId }, { "address", ipAddr }, { "last_updated", std::to_string(ts) } }; - tx.zadd("nodes-online:{" + controllerId + "}", memberId, ts) - .zadd("nodes-online2:{" + controllerId + "}", networkId + "-" + memberId, ts) - .zadd("network-nodes-online:{" + controllerId + "}:" + networkId, memberId, ts) - .zadd("active-networks:{" + controllerId + "}", networkId, ts) - .sadd("network-nodes-all:{" + controllerId + "}:" + networkId, memberId) - .hmset("member:{" + controllerId + "}:" + networkId + ":" + memberId, record.begin(), record.end()); - ++count; - Metrics::redis_node_checkin++; - } - - // expire records from all-nodes and network-nodes member list - uint64_t expireOld = OSUtils::now() - 300000; - - tx.zremrangebyscore("nodes-online:{" + controllerId + "}", sw::redis::RightBoundedInterval(expireOld, sw::redis::BoundType::LEFT_OPEN)); - tx.zremrangebyscore("nodes-online2:{" + controllerId + "}", sw::redis::RightBoundedInterval(expireOld, sw::redis::BoundType::LEFT_OPEN)); - tx.zremrangebyscore("active-networks:{" + controllerId + "}", sw::redis::RightBoundedInterval(expireOld, sw::redis::BoundType::LEFT_OPEN)); - { - std::shared_lock l(_networks_l); - for (const auto& it : _networks) { - uint64_t nwid_i = it.first; - char nwidTmp[64]; - OSUtils::ztsnprintf(nwidTmp, sizeof(nwidTmp), "%.16llx", nwid_i); - tx.zremrangebyscore("network-nodes-online:{" + controllerId + "}:" + nwidTmp, sw::redis::RightBoundedInterval(expireOld, sw::redis::BoundType::LEFT_OPEN)); - } - } - tx.exec(); - fprintf(stderr, "%s: Updated online status of %d members\n", _myAddressStr.c_str(), count); - - return count; -} - -#endif // ZT_CONTROLLER_USE_LIBPQ diff --git a/controller/CV1.hpp b/controller/CV1.hpp deleted file mode 100644 index 549b17912..000000000 --- a/controller/CV1.hpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#include "DB.hpp" - -#ifdef ZT_CONTROLLER_USE_LIBPQ - -#ifndef ZT_CONTROLLER_CV1_HPP -#define ZT_CONTROLLER_CV1_HPP - -#define ZT_CENTRAL_CONTROLLER_COMMIT_THREADS 4 - -#include "../node/Metrics.hpp" -#include "ConnectionPool.hpp" -#include "PostgreSQL.hpp" - -#include -#include -#include - -namespace smeeclient { -struct SmeeClient; -} - -namespace ZeroTier { - -struct RedisConfig; - -/** - * A controller database driver that talks to PostgreSQL - * - * This is for use with ZeroTier Central. Others are free to build and use it - * but be aware that we might change it at any time. - */ -class CV1 : public DB { - public: - CV1(const Identity& myId, const char* path, int listenPort, RedisConfig* rc); - virtual ~CV1(); - - virtual bool waitForReady(); - virtual bool isReady(); - virtual bool save(nlohmann::json& record, bool notifyListeners); - virtual void eraseNetwork(const uint64_t networkId); - virtual void eraseMember(const uint64_t networkId, const uint64_t memberId); - virtual void nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress); - virtual void nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress, const char* osArch); - virtual AuthInfo getSSOAuthInfo(const nlohmann::json& member, const std::string& redirectURL); - - virtual bool ready() - { - return _ready == 2; - } - - protected: - struct _PairHasher { - inline std::size_t operator()(const std::pair& p) const - { - return (std::size_t)(p.first ^ p.second); - } - }; - virtual void _memberChanged(nlohmann::json& old, nlohmann::json& memberConfig, bool notifyListeners) - { - DB::_memberChanged(old, memberConfig, notifyListeners); - } - - virtual void _networkChanged(nlohmann::json& old, nlohmann::json& networkConfig, bool notifyListeners) - { - DB::_networkChanged(old, networkConfig, notifyListeners); - } - - private: - void initializeNetworks(); - void initializeMembers(); - void heartbeat(); - void membersDbWatcher(); - void _membersWatcher_Postgres(); - void networksDbWatcher(); - void _networksWatcher_Postgres(); - - void _membersWatcher_Redis(); - void _networksWatcher_Redis(); - - void commitThread(); - void onlineNotificationThread(); - void onlineNotification_Postgres(); - void onlineNotification_Redis(); - uint64_t _doRedisUpdate(sw::redis::Transaction& tx, std::string& controllerId, std::unordered_map, NodeOnlineRecord, _PairHasher>& lastOnline); - - void configureSmee(); - void notifyNewMember(const std::string& networkID, const std::string& memberID); - - enum OverrideMode { ALLOW_PGBOUNCER_OVERRIDE = 0, NO_OVERRIDE = 1 }; - - std::shared_ptr > _pool; - - const Identity _myId; - const Address _myAddress; - std::string _myAddressStr; - std::string _connString; - - BlockingQueue > _commitQueue; - - std::thread _heartbeatThread; - std::thread _membersDbWatcher; - std::thread _networksDbWatcher; - std::thread _commitThread[ZT_CENTRAL_CONTROLLER_COMMIT_THREADS]; - std::thread _onlineNotificationThread; - - std::unordered_map, NodeOnlineRecord, _PairHasher> _lastOnline; - - mutable std::mutex _lastOnline_l; - mutable std::mutex _readyLock; - std::atomic _ready, _connected, _run; - mutable volatile bool _waitNoticePrinted; - - int _listenPort; - uint8_t _ssoPsk[48]; - - RedisConfig* _rc; - std::shared_ptr _redis; - std::shared_ptr _cluster; - bool _redisMemberStatus; - - smeeclient::SmeeClient* _smee; -}; - -} // namespace ZeroTier - -#endif // ZT_CONTROLLER_CV1_HPP - -#endif // ZT_CONTROLLER_USE_LIBPQ diff --git a/controller/CV2.cpp b/controller/CV2.cpp deleted file mode 100644 index 9b3849737..000000000 --- a/controller/CV2.cpp +++ /dev/null @@ -1,1104 +0,0 @@ -/* - * Copyright (c)2025 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#include "CV2.hpp" - -#ifdef ZT_CONTROLLER_USE_LIBPQ - -#include "../node/Constants.hpp" -#include "../node/SHA512.hpp" -#include "../version.h" -#include "CtlUtil.hpp" -#include "EmbeddedNetworkController.hpp" - -#include -#include -#include -#include -#include - -using json = nlohmann::json; - -namespace { - -} - -using namespace ZeroTier; - -CV2::CV2(const Identity& myId, const char* path, int listenPort) : DB(), _pool(), _myId(myId), _myAddress(myId.address()), _ready(0), _connected(1), _run(1), _waitNoticePrinted(false), _listenPort(listenPort) -{ - fprintf(stderr, "CV2::CV2\n"); - char myAddress[64]; - _myAddressStr = myId.address().toString(myAddress); - - _connString = std::string(path); - - auto f = std::make_shared(_connString); - _pool = std::make_shared >(15, 5, std::static_pointer_cast(f)); - - memset(_ssoPsk, 0, sizeof(_ssoPsk)); - char* const ssoPskHex = getenv("ZT_SSO_PSK"); -#ifdef ZT_TRACE - fprintf(stderr, "ZT_SSO_PSK: %s\n", ssoPskHex); -#endif - if (ssoPskHex) { - // SECURITY: note that ssoPskHex will always be null-terminated if libc actually - // returns something non-NULL. If the hex encodes something shorter than 48 bytes, - // it will be padded at the end with zeroes. If longer, it'll be truncated. - Utils::unhex(ssoPskHex, _ssoPsk, sizeof(_ssoPsk)); - } - - _readyLock.lock(); - - fprintf(stderr, "[%s] NOTICE: %.10llx controller PostgreSQL waiting for initial data download..." ZT_EOL_S, ::_timestr(), (unsigned long long)_myAddress.toInt()); - _waitNoticePrinted = true; - - initializeNetworks(); - initializeMembers(); - - _heartbeatThread = std::thread(&CV2::heartbeat, this); - _membersDbWatcher = std::thread(&CV2::membersDbWatcher, this); - _networksDbWatcher = std::thread(&CV2::networksDbWatcher, this); - for (int i = 0; i < ZT_CENTRAL_CONTROLLER_COMMIT_THREADS; ++i) { - _commitThread[i] = std::thread(&CV2::commitThread, this); - } - _onlineNotificationThread = std::thread(&CV2::onlineNotificationThread, this); -} - -CV2::~CV2() -{ - _run = 0; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - _heartbeatThread.join(); - _membersDbWatcher.join(); - _networksDbWatcher.join(); - _commitQueue.stop(); - for (int i = 0; i < ZT_CENTRAL_CONTROLLER_COMMIT_THREADS; ++i) { - _commitThread[i].join(); - } - _onlineNotificationThread.join(); -} - -bool CV2::waitForReady() -{ - while (_ready < 2) { - _readyLock.lock(); - _readyLock.unlock(); - } - return true; -} - -bool CV2::isReady() -{ - return (_ready == 2) && _connected; -} - -bool CV2::save(nlohmann::json& record, bool notifyListeners) -{ - bool modified = false; - try { - if (! record.is_object()) { - fprintf(stderr, "record is not an object?!?\n"); - return false; - } - const std::string objtype = record["objtype"]; - if (objtype == "network") { - // fprintf(stderr, "network save\n"); - const uint64_t nwid = OSUtils::jsonIntHex(record["id"], 0ULL); - if (nwid) { - nlohmann::json old; - get(nwid, old); - if ((! old.is_object()) || (! _compareRecords(old, record))) { - record["revision"] = OSUtils::jsonInt(record["revision"], 0ULL) + 1ULL; - _commitQueue.post(std::pair(record, notifyListeners)); - modified = true; - } - } - } - else if (objtype == "member") { - std::string networkId = record["nwid"]; - std::string memberId = record["id"]; - const uint64_t nwid = OSUtils::jsonIntHex(record["nwid"], 0ULL); - const uint64_t id = OSUtils::jsonIntHex(record["id"], 0ULL); - // fprintf(stderr, "member save %s-%s\n", networkId.c_str(), memberId.c_str()); - if ((id) && (nwid)) { - nlohmann::json network, old; - get(nwid, network, id, old); - if ((! old.is_object()) || (! _compareRecords(old, record))) { - // fprintf(stderr, "commit queue post\n"); - record["revision"] = OSUtils::jsonInt(record["revision"], 0ULL) + 1ULL; - _commitQueue.post(std::pair(record, notifyListeners)); - modified = true; - } - else { - // fprintf(stderr, "no change\n"); - } - } - } - else { - fprintf(stderr, "uhh waaat\n"); - } - } - catch (std::exception& e) { - fprintf(stderr, "Error on PostgreSQL::save: %s\n", e.what()); - } - catch (...) { - fprintf(stderr, "Unknown error on PostgreSQL::save\n"); - } - return modified; -} - -void CV2::eraseNetwork(const uint64_t networkId) -{ - fprintf(stderr, "PostgreSQL::eraseNetwork\n"); - char tmp2[24]; - waitForReady(); - Utils::hex(networkId, tmp2); - std::pair tmp; - tmp.first["id"] = tmp2; - tmp.first["objtype"] = "_delete_network"; - tmp.second = true; - _commitQueue.post(tmp); - nlohmann::json nullJson; - _networkChanged(tmp.first, nullJson, true); -} - -void CV2::eraseMember(const uint64_t networkId, const uint64_t memberId) -{ - fprintf(stderr, "PostgreSQL::eraseMember\n"); - char tmp2[24]; - waitForReady(); - std::pair tmp, nw; - Utils::hex(networkId, tmp2); - tmp.first["nwid"] = tmp2; - Utils::hex(memberId, tmp2); - tmp.first["id"] = tmp2; - tmp.first["objtype"] = "_delete_member"; - tmp.second = true; - _commitQueue.post(tmp); - nlohmann::json nullJson; - _memberChanged(tmp.first, nullJson, true); -} - -void CV2::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress, const char* osArch) -{ - std::lock_guard l(_lastOnline_l); - NodeOnlineRecord& i = _lastOnline[std::pair(networkId, memberId)]; - i.lastSeen = OSUtils::now(); - if (physicalAddress) { - i.physicalAddress = physicalAddress; - } - i.osArch = std::string(osArch); -} - -void CV2::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress) -{ - this->nodeIsOnline(networkId, memberId, physicalAddress, "unknown/unknown"); -} - -AuthInfo CV2::getSSOAuthInfo(const nlohmann::json& member, const std::string& redirectURL) -{ - // TODO: Redo this for CV2 - - Metrics::db_get_sso_info++; - // NONCE is just a random character string. no semantic meaning - // state = HMAC SHA384 of Nonce based on shared sso key - // - // need nonce timeout in database? make sure it's used within X time - // X is 5 minutes for now. Make configurable later? - // - // how do we tell when a nonce is used? if auth_expiration_time is set - std::string networkId = member["nwid"]; - std::string memberId = member["id"]; - - char authenticationURL[4096] = { 0 }; - AuthInfo info; - info.enabled = true; - - // if (memberId == "a10dccea52" && networkId == "8056c2e21c24673d") { - // fprintf(stderr, "invalid authinfo for grant's machine\n"); - // info.version=1; - // return info; - // } - // fprintf(stderr, "PostgreSQL::updateMemberOnLoad: %s-%s\n", networkId.c_str(), memberId.c_str()); - std::shared_ptr c; - try { - // c = _pool->borrow(); - // pqxx::work w(*c->c); - - // char nonceBytes[16] = {0}; - // std::string nonce = ""; - - // // check if the member exists first. - // pqxx::row count = w.exec_params1("SELECT count(id) FROM ztc_member WHERE id = $1 AND network_id = $2 AND deleted = false", memberId, networkId); - // if (count[0].as() == 1) { - // // get active nonce, if exists. - // pqxx::result r = w.exec_params("SELECT nonce FROM ztc_sso_expiry " - // "WHERE network_id = $1 AND member_id = $2 " - // "AND ((NOW() AT TIME ZONE 'UTC') <= authentication_expiry_time) AND ((NOW() AT TIME ZONE 'UTC') <= nonce_expiration)", - // networkId, memberId); - - // if (r.size() == 0) { - // // no active nonce. - // // find an unused nonce, if one exists. - // pqxx::result r = w.exec_params("SELECT nonce FROM ztc_sso_expiry " - // "WHERE network_id = $1 AND member_id = $2 " - // "AND authentication_expiry_time IS NULL AND ((NOW() AT TIME ZONE 'UTC') <= nonce_expiration)", - // networkId, memberId); - - // if (r.size() == 1) { - // // we have an existing nonce. Use it - // nonce = r.at(0)[0].as(); - // Utils::unhex(nonce.c_str(), nonceBytes, sizeof(nonceBytes)); - // } else if (r.empty()) { - // // create a nonce - // Utils::getSecureRandom(nonceBytes, 16); - // char nonceBuf[64] = {0}; - // Utils::hex(nonceBytes, sizeof(nonceBytes), nonceBuf); - // nonce = std::string(nonceBuf); - - // pqxx::result ir = w.exec_params0("INSERT INTO ztc_sso_expiry " - // "(nonce, nonce_expiration, network_id, member_id) VALUES " - // "($1, TO_TIMESTAMP($2::double precision/1000), $3, $4)", - // nonce, OSUtils::now() + 300000, networkId, memberId); - - // w.commit(); - // } else { - // // > 1 ?!? Thats an error! - // fprintf(stderr, "> 1 unused nonce!\n"); - // exit(6); - // } - // } else if (r.size() == 1) { - // nonce = r.at(0)[0].as(); - // Utils::unhex(nonce.c_str(), nonceBytes, sizeof(nonceBytes)); - // } else { - // // more than 1 nonce in use? Uhhh... - // fprintf(stderr, "> 1 nonce in use for network member?!?\n"); - // exit(7); - // } - - // r = w.exec_params( - // "SELECT oc.client_id, oc.authorization_endpoint, oc.issuer, oc.provider, oc.sso_impl_version " - // "FROM ztc_network AS n " - // "INNER JOIN ztc_org o " - // " ON o.owner_id = n.owner_id " - // "LEFT OUTER JOIN ztc_network_oidc_config noc " - // " ON noc.network_id = n.id " - // "LEFT OUTER JOIN ztc_oidc_config oc " - // " ON noc.client_id = oc.client_id AND oc.org_id = o.org_id " - // "WHERE n.id = $1 AND n.sso_enabled = true", networkId); - - // std::string client_id = ""; - // std::string authorization_endpoint = ""; - // std::string issuer = ""; - // std::string provider = ""; - // uint64_t sso_version = 0; - - // if (r.size() == 1) { - // client_id = r.at(0)[0].as>().value_or(""); - // authorization_endpoint = r.at(0)[1].as>().value_or(""); - // issuer = r.at(0)[2].as>().value_or(""); - // provider = r.at(0)[3].as>().value_or(""); - // sso_version = r.at(0)[4].as>().value_or(1); - // } else if (r.size() > 1) { - // fprintf(stderr, "ERROR: More than one auth endpoint for an organization?!?!? NetworkID: %s\n", networkId.c_str()); - // } else { - // fprintf(stderr, "No client or auth endpoint?!?\n"); - // } - - // info.version = sso_version; - - // // no catch all else because we don't actually care if no records exist here. just continue as normal. - // if ((!client_id.empty())&&(!authorization_endpoint.empty())) { - - // uint8_t state[48]; - // HMACSHA384(_ssoPsk, nonceBytes, sizeof(nonceBytes), state); - // char state_hex[256]; - // Utils::hex(state, 48, state_hex); - - // if (info.version == 0) { - // char url[2048] = {0}; - // OSUtils::ztsnprintf(url, sizeof(authenticationURL), - // "%s?response_type=id_token&response_mode=form_post&scope=openid+email+profile&redirect_uri=%s&nonce=%s&state=%s&client_id=%s", - // authorization_endpoint.c_str(), - // url_encode(redirectURL).c_str(), - // nonce.c_str(), - // state_hex, - // client_id.c_str()); - // info.authenticationURL = std::string(url); - // } else if (info.version == 1) { - // info.ssoClientID = client_id; - // info.issuerURL = issuer; - // info.ssoProvider = provider; - // info.ssoNonce = nonce; - // info.ssoState = std::string(state_hex) + "_" +networkId; - // info.centralAuthURL = redirectURL; - // #ifdef ZT_DEBUG - // fprintf( - // stderr, - // "ssoClientID: %s\nissuerURL: %s\nssoNonce: %s\nssoState: %s\ncentralAuthURL: %s\nprovider: %s\n", - // info.ssoClientID.c_str(), - // info.issuerURL.c_str(), - // info.ssoNonce.c_str(), - // info.ssoState.c_str(), - // info.centralAuthURL.c_str(), - // provider.c_str()); - // #endif - // } - // } else { - // fprintf(stderr, "client_id: %s\nauthorization_endpoint: %s\n", client_id.c_str(), authorization_endpoint.c_str()); - // } - // } - - // _pool->unborrow(c); - } - catch (std::exception& e) { - fprintf(stderr, "ERROR: Error updating member on load for network %s: %s\n", networkId.c_str(), e.what()); - } - - return info; // std::string(authenticationURL); -} - -void CV2::initializeNetworks() -{ - fprintf(stderr, "Initializing networks...\n"); - - try { - char qbuf[2048]; - sprintf( - qbuf, - "SELECT id, name, configuration , (EXTRACT(EPOCH FROM creation_time AT TIME ZONE 'UTC')*1000)::bigint, " - "(EXTRACT(EPOCH FROM last_modified AT TIME ZONE 'UTC')*1000)::bigint, revision " - "FROM networks_ctl WHERE controller_id = '%s'", - _myAddressStr.c_str()); - - auto c = _pool->borrow(); - pqxx::work w(*c->c); - - fprintf(stderr, "Load networks from psql...\n"); - auto stream = pqxx::stream_from::query(w, qbuf); - std::tuple< - std::string // network ID - , - std::optional // name - , - std::string // configuration - , - std::optional // creation_time - , - std::optional // last_modified - , - std::optional // revision - > - row; - uint64_t count = 0; - uint64_t total = 0; - while (stream >> row) { - auto start = std::chrono::high_resolution_clock::now(); - - json empty; - json config; - - initNetwork(config); - - std::string nwid = std::get<0>(row); - std::string name = std::get<1>(row).value_or(""); - json cfgtmp = json::parse(std::get<2>(row)); - std::optional created_at = std::get<3>(row); - std::optional last_modified = std::get<4>(row); - std::optional revision = std::get<5>(row); - - config["id"] = nwid; - config["name"] = name; - config["creationTime"] = created_at.value_or(0); - config["lastModified"] = last_modified.value_or(0); - config["revision"] = revision.value_or(0); - config["capabilities"] = cfgtmp["capabilities"].is_array() ? cfgtmp["capabilities"] : json::array(); - config["enableBroadcast"] = cfgtmp["enableBroadcast"].is_boolean() ? cfgtmp["enableBroadcast"].get() : false; - config["mtu"] = cfgtmp["mtu"].is_number() ? cfgtmp["mtu"].get() : 2800; - config["multicastLimit"] = cfgtmp["multicastLimit"].is_number() ? cfgtmp["multicastLimit"].get() : 64; - config["private"] = cfgtmp["private"].is_boolean() ? cfgtmp["private"].get() : true; - config["remoteTraceLevel"] = cfgtmp["remoteTraceLevel"].is_number() ? cfgtmp["remoteTraceLevel"].get() : 0; - config["remoteTraceTarget"] = cfgtmp["remoteTraceTarget"].is_string() ? cfgtmp["remoteTraceTarget"].get() : ""; - config["revision"] = revision.value_or(0); - config["rules"] = cfgtmp["rules"].is_array() ? cfgtmp["rules"] : json::array(); - config["tags"] = cfgtmp["tags"].is_array() ? cfgtmp["tags"] : json::array(); - if (cfgtmp["v4AssignMode"].is_object()) { - config["v4AssignMode"] = cfgtmp["v4AssignMode"]; - } - else { - config["v4AssignMode"] = json::object(); - config["v4AssignMode"]["zt"] = true; - } - if (cfgtmp["v6AssignMode"].is_object()) { - config["v6AssignMode"] = cfgtmp["v6AssignMode"]; - } - else { - config["v6AssignMode"] = json::object(); - config["v6AssignMode"]["zt"] = true; - config["v6AssignMode"]["6plane"] = true; - config["v6AssignMode"]["rfc4193"] = false; - } - config["ssoEnabled"] = cfgtmp["ssoEnabled"].is_boolean() ? cfgtmp["ssoEnabled"].get() : false; - config["objtype"] = "network"; - config["routes"] = cfgtmp["routes"].is_array() ? cfgtmp["routes"] : json::array(); - config["clientId"] = cfgtmp["clientId"].is_string() ? cfgtmp["clientId"].get() : ""; - config["authorizationEndpoint"] = cfgtmp["authorizationEndpoint"].is_string() ? cfgtmp["authorizationEndpoint"].get() : nullptr; - config["provider"] = cfgtmp["ssoProvider"].is_string() ? cfgtmp["ssoProvider"].get() : ""; - if (! cfgtmp["dns"].is_object()) { - cfgtmp["dns"] = json::object(); - cfgtmp["dns"]["domain"] = ""; - cfgtmp["dns"]["servers"] = json::array(); - } - else { - config["dns"] = cfgtmp["dns"]; - } - config["ipAssignmentPools"] = cfgtmp["ipAssignmentPools"].is_array() ? cfgtmp["ipAssignmentPools"] : json::array(); - - Metrics::network_count++; - - _networkChanged(empty, config, false); - - auto end = std::chrono::high_resolution_clock::now(); - auto dur = std::chrono::duration_cast(end - start); - ; - total += dur.count(); - ++count; - if (count > 0 && count % 10000 == 0) { - fprintf(stderr, "Averaging %lu us per network\n", (total / count)); - } - } - - w.commit(); - _pool->unborrow(c); - fprintf(stderr, "done.\n"); - - if (++this->_ready == 2) { - if (_waitNoticePrinted) { - fprintf(stderr, "[%s] NOTICE: %.10llx controller PostgreSQL data download complete." ZT_EOL_S, _timestr(), (unsigned long long)_myAddress.toInt()); - } - _readyLock.unlock(); - } - fprintf(stderr, "network init done\n"); - } - catch (std::exception& e) { - fprintf(stderr, "ERROR: Error initializing networks: %s\n", e.what()); - std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - exit(-1); - } -} - -void CV2::initializeMembers() -{ - std::string memberId; - std::string networkId; - try { - char qbuf[2048]; - sprintf( - qbuf, - "SELECT nm.device_id, nm.network_id, nm.authorized, nm.active_bridge, nm.ip_assignments, nm.no_auto_assign_ips, " - "nm.sso_exempt, (EXTRACT(EPOCH FROM nm.authentication_expiry_time AT TIME ZONE 'UTC')*1000)::bigint, " - "(EXTRACT(EPOCH FROM nm.creation_time AT TIME ZONE 'UTC')*1000)::bigint, nm.identity, " - "(EXTRACT(EPOCH FROM nm.last_authorized_time AT TIME ZONE 'UTC')*1000)::bigint, " - "(EXTRACT(EPOCH FROM nm.last_deauthorized_time AT TIME ZONE 'UTC')*1000)::bigint, " - "nm.remote_trace_level, nm.remote_trace_target, nm.revision, nm.capabilities, nm.tags " - "FROM network_memberships_ctl nm " - "INNER JOIN networks_ctl n " - " ON nm.network_id = n.id " - "WHERE n.controller_id = '%s'", - _myAddressStr.c_str()); - - auto c = _pool->borrow(); - pqxx::work w(*c->c); - fprintf(stderr, "Load members from psql...\n"); - auto stream = pqxx::stream_from::query(w, qbuf); - std::tuple< - std::string // device ID - , - std::string // network ID - , - bool // authorized - , - std::optional // active_bridge - , - std::optional // ip_assignments - , - std::optional // no_auto_assign_ips - , - std::optional // sso_exempt - , - std::optional // authentication_expiry_time - , - std::optional // creation_time - , - std::optional // identity - , - std::optional // last_authorized_time - , - std::optional // last_deauthorized_time - , - std::optional // remote_trace_level - , - std::optional // remote_trace_target - , - std::optional // revision - , - std::optional // capabilities - , - std::optional // tags - > - row; - - uint64_t count = 0; - uint64_t total = 0; - while (stream >> row) { - auto start = std::chrono::high_resolution_clock::now(); - json empty; - json config; - - initMember(config); - - memberId = std::get<0>(row); - networkId = std::get<1>(row); - bool authorized = std::get<2>(row); - std::optional active_bridge = std::get<3>(row); - std::string ip_assignments = std::get<4>(row).value_or(""); - std::optional no_auto_assign_ips = std::get<5>(row); - std::optional sso_exempt = std::get<6>(row); - std::optional authentication_expiry_time = std::get<7>(row); - std::optional creation_time = std::get<8>(row); - std::optional identity = std::get<9>(row); - std::optional last_authorized_time = std::get<10>(row); - std::optional last_deauthorized_time = std::get<11>(row); - std::optional remote_trace_level = std::get<12>(row); - std::optional remote_trace_target = std::get<13>(row); - std::optional revision = std::get<14>(row); - std::optional capabilities = std::get<15>(row); - std::optional tags = std::get<16>(row); - - config["objtype"] = "member"; - config["id"] = memberId; - config["address"] = identity.value_or(""); - config["nwid"] = networkId; - config["authorized"] = authorized; - config["activeBridge"] = active_bridge.value_or(false); - config["ipAssignments"] = json::array(); - if (ip_assignments != "{}") { - std::string tmp = ip_assignments.substr(1, ip_assignments.length() - 2); - std::vector addrs = split(tmp, ','); - for (auto it = addrs.begin(); it != addrs.end(); ++it) { - config["ipAssignments"].push_back(*it); - } - } - config["capabilities"] = json::parse(capabilities.value_or("[]")); - config["creationTime"] = creation_time.value_or(0); - config["lastAuthorizedTime"] = last_authorized_time.value_or(0); - config["lastDeauthorizedTime"] = last_deauthorized_time.value_or(0); - config["noAutoAssignIPs"] = no_auto_assign_ips.value_or(false); - config["remoteTraceLevel"] = remote_trace_level.value_or(0); - config["remoteTraceTarget"] = remote_trace_target.value_or(nullptr); - config["revision"] = revision.value_or(0); - config["ssoExempt"] = sso_exempt.value_or(false); - config["authenticationExpiryTime"] = authentication_expiry_time.value_or(0); - config["tags"] = json::parse(tags.value_or("[]")); - - Metrics::member_count++; - - _memberChanged(empty, config, false); - - memberId = ""; - networkId = ""; - - auto end = std::chrono::high_resolution_clock::now(); - auto dur = std::chrono::duration_cast(end - start); - total += dur.count(); - ++count; - if (count > 0 && count % 10000 == 0) { - fprintf(stderr, "Averaging %lu us per member\n", (total / count)); - } - } - if (count > 0) { - fprintf(stderr, "Took %lu us per member to load\n", (total / count)); - } - - stream.complete(); - w.commit(); - _pool->unborrow(c); - fprintf(stderr, "done.\n"); - - if (++this->_ready == 2) { - if (_waitNoticePrinted) { - fprintf(stderr, "[%s] NOTICE: %.10llx controller PostgreSQL data download complete." ZT_EOL_S, _timestr(), (unsigned long long)_myAddress.toInt()); - } - _readyLock.unlock(); - } - fprintf(stderr, "member init done\n"); - } - catch (std::exception& e) { - fprintf(stderr, "ERROR: Error initializing member: %s-%s %s\n", networkId.c_str(), memberId.c_str(), e.what()); - exit(-1); - } -} - -void CV2::heartbeat() -{ - char publicId[1024]; - char hostnameTmp[1024]; - _myId.toString(false, publicId); - if (gethostname(hostnameTmp, sizeof(hostnameTmp)) != 0) { - hostnameTmp[0] = (char)0; - } - else { - for (int i = 0; i < (int)sizeof(hostnameTmp); ++i) { - if ((hostnameTmp[i] == '.') || (hostnameTmp[i] == 0)) { - hostnameTmp[i] = (char)0; - break; - } - } - } - const char* controllerId = _myAddressStr.c_str(); - const char* publicIdentity = publicId; - const char* hostname = hostnameTmp; - - while (_run == 1) { - auto c = _pool->borrow(); - int64_t ts = OSUtils::now(); - - if (c->c) { - std::string major = std::to_string(ZEROTIER_ONE_VERSION_MAJOR); - std::string minor = std::to_string(ZEROTIER_ONE_VERSION_MINOR); - std::string rev = std::to_string(ZEROTIER_ONE_VERSION_REVISION); - std::string version = major + "." + minor + "." + rev; - std::string versionStr = "v" + version; - - try { - pqxx::work w { *c->c }; - w.exec_params0( - "INSERT INTO controllers_ctl (id, hostname, last_heartbeat, public_identity, version) VALUES " - "($1, $2, TO_TIMESTAMP($3::double precision/1000), $4, $5) " - "ON CONFLICT (id) DO UPDATE SET hostname = EXCLUDED.hostname, last_heartbeat = EXCLUDED.last_heartbeat, " - "public_identity = EXCLUDED.public_identity, version = EXCLUDED.version", - controllerId, - hostname, - ts, - publicIdentity, - versionStr); - w.commit(); - } - catch (std::exception& e) { - fprintf(stderr, "ERROR: Error in heartbeat: %s\n", e.what()); - continue; - } - catch (...) { - fprintf(stderr, "ERROR: Unknown error in heartbeat\n"); - continue; - } - } - - _pool->unborrow(c); - - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - fprintf(stderr, "Exited heartbeat thread\n"); -} - -void CV2::membersDbWatcher() -{ - auto c = _pool->borrow(); - - std::string stream = "member_" + _myAddressStr; - - fprintf(stderr, "Listening to member stream: %s\n", stream.c_str()); - MemberNotificationReceiver m(this, *c->c, stream); - - while (_run == 1) { - c->c->await_notification(5, 0); - } - - _pool->unborrow(c); - - fprintf(stderr, "Exited membersDbWatcher\n"); -} - -void CV2::networksDbWatcher() -{ - std::string stream = "network_" + _myAddressStr; - - fprintf(stderr, "Listening to member stream: %s\n", stream.c_str()); - - auto c = _pool->borrow(); - - NetworkNotificationReceiver n(this, *c->c, stream); - - while (_run == 1) { - c->c->await_notification(5, 0); - } - - _pool->unborrow(c); - fprintf(stderr, "Exited networksDbWatcher\n"); -} - -void CV2::commitThread() -{ - fprintf(stderr, "%s: commitThread start\n", _myAddressStr.c_str()); - std::pair qitem; - while (_commitQueue.get(qitem) && (_run == 1)) { - // fprintf(stderr, "commitThread tick\n"); - if (! qitem.first.is_object()) { - fprintf(stderr, "not an object\n"); - continue; - } - - std::shared_ptr c; - try { - c = _pool->borrow(); - } - catch (std::exception& e) { - fprintf(stderr, "ERROR: %s\n", e.what()); - continue; - } - - if (! c) { - fprintf(stderr, "Error getting database connection\n"); - continue; - } - - Metrics::pgsql_commit_ticks++; - try { - nlohmann::json& config = (qitem.first); - const std::string objtype = config["objtype"]; - if (objtype == "member") { - // fprintf(stderr, "%s: commitThread: member\n", _myAddressStr.c_str()); - std::string memberId; - std::string networkId; - try { - pqxx::work w(*c->c); - - memberId = config["id"]; - networkId = config["nwid"]; - - std::string target = "NULL"; - if (! config["remoteTraceTarget"].is_null()) { - target = config["remoteTraceTarget"]; - } - - pqxx::row nwrow = w.exec_params1("SELECT COUNT(id) FROM networks WHERE id = $1", networkId); - int nwcount = nwrow[0].as(); - - if (nwcount != 1) { - fprintf(stderr, "network %s does not exist. skipping member upsert\n", networkId.c_str()); - w.abort(); - _pool->unborrow(c); - continue; - } - - // only needed for hooks, and no hooks for now - // pqxx::row mrow = w.exec_params1("SELECT COUNT(id) FROM device_networks WHERE device_id = $1 AND network_id = $2", memberId, networkId); - // int membercount = mrow[0].as(); - // bool isNewMember = (membercount == 0); - - pqxx::result res = w.exec_params0( - "INSERT INTO network_memberships_ctl (device_id, network_id, authorized, active_bridge, ip_assignments, " - "no_auto_assign_ips, sso_exempt, authentication_expiry_time, capabilities, creation_time, " - "identity, last_authorized_time, last_deauthorized_time, " - "remote_trace_level, remote_trace_target, revision, tags, version_major, version_minor, " - "version_revision, version_protocol) " - "VALUES ($1, $2, $3, $4, $5, $6, $7, TO_TIMESTAMP($8::double precision/1000), $9, " - "TO_TIMESTAMP($10::double precision/1000), $11, TO_TIMESTAMP($12::double precision/1000), " - "TO_TIMESTAMP($13::double precision/1000), $14, $15, $16, $17, $18, $19, $20, $21) " - "ON CONFLICT (device_id, network_id) DO UPDATE SET " - "authorized = EXCLUDED.authorized, active_bridge = EXCLUDED.active_bridge, " - "ip_assignments = EXCLUDED.ip_assignments, no_auto_assign_ips = EXCLUDED.no_auto_assign_ips, " - "sso_exempt = EXCLUDED.sso_exempt, authentication_expiry_time = EXCLUDED.authentication_expiry_time, " - "capabilities = EXCLUDED.capabilities, creation_time = EXCLUDED.creation_time, " - "identity = EXCLUDED.identity, last_authorized_time = EXCLUDED.last_authorized_time, " - "last_deauthorized_time = EXCLUDED.last_deauthorized_time, " - "remote_trace_level = EXCLUDED.remote_trace_level, remote_trace_target = EXCLUDED.remote_trace_target, " - "revision = EXCLUDED.revision, tags = EXCLUDED.tags, version_major = EXCLUDED.version_major, " - "version_minor = EXCLUDED.version_minor, version_revision = EXCLUDED.version_revision, " - "version_protocol = EXCLUDED.version_protocol", - memberId, - networkId, - (bool)config["authorized"], - (bool)config["activeBridge"], - config["ipAssignments"].get >(), - (bool)config["noAutoAssignIps"], - (bool)config["ssoExempt"], - (uint64_t)config["authenticationExpiryTime"], - OSUtils::jsonDump(config["capabilities"], -1), - (uint64_t)config["creationTime"], - OSUtils::jsonString(config["identity"], ""), - (uint64_t)config["lastAuthorizedTime"], - (uint64_t)config["lastDeauthorizedTime"], - (int)config["remoteTraceLevel"], - target, - (uint64_t)config["revision"], - OSUtils::jsonDump(config["tags"], -1), - (int)config["vMajor"], - (int)config["vMinor"], - (int)config["vRev"], - (int)config["vProto"]); - - w.commit(); - - // No hooks for now - // if (_smee != NULL && isNewMember) { - // pqxx::row row = w.exec_params1( - // "SELECT " - // " count(h.hook_id) " - // "FROM " - // " ztc_hook h " - // " INNER JOIN ztc_org o ON o.org_id = h.org_id " - // " INNER JOIN ztc_network n ON n.owner_id = o.owner_id " - // " WHERE " - // "n.id = $1 ", - // networkId - // ); - // int64_t hookCount = row[0].as(); - // if (hookCount > 0) { - // notifyNewMember(networkId, memberId); - // } - // } - - const uint64_t nwidInt = OSUtils::jsonIntHex(config["nwid"], 0ULL); - const uint64_t memberidInt = OSUtils::jsonIntHex(config["id"], 0ULL); - if (nwidInt && memberidInt) { - nlohmann::json nwOrig; - nlohmann::json memOrig; - - nlohmann::json memNew(config); - - get(nwidInt, nwOrig, memberidInt, memOrig); - - _memberChanged(memOrig, memNew, qitem.second); - } - else { - fprintf(stderr, "%s: Can't notify of change. Error parsing nwid or memberid: %llu-%llu\n", _myAddressStr.c_str(), (unsigned long long)nwidInt, (unsigned long long)memberidInt); - } - } - catch (pqxx::data_exception& e) { - std::string cfgDump = OSUtils::jsonDump(config, 2); - fprintf(stderr, "Member save %s-%s: %s\n", networkId.c_str(), memberId.c_str(), cfgDump.c_str()); - - const pqxx::sql_error* s = dynamic_cast(&e); - fprintf(stderr, "%s ERROR: Error updating member: %s\n", _myAddressStr.c_str(), e.what()); - if (s) { - fprintf(stderr, "%s ERROR: SQL error: %s\n", _myAddressStr.c_str(), s->query().c_str()); - } - } - catch (std::exception& e) { - std::string cfgDump = OSUtils::jsonDump(config, 2); - fprintf(stderr, "%s ERROR: Error updating member %s-%s: %s\njsonDump: %s\n", _myAddressStr.c_str(), networkId.c_str(), memberId.c_str(), e.what(), cfgDump.c_str()); - } - } - else if (objtype == "network") { - try { - // fprintf(stderr, "%s: commitThread: network\n", _myAddressStr.c_str()); - pqxx::work w(*c->c); - - std::string id = config["id"]; - - // network must already exist - pqxx::result res = w.exec_params0( - "INSERT INTO networks_ctl (id, name, configuration, controller_id, revision) " - "VALUES ($1, $2, $3, $4, $5) " - "ON CONFLICT (id) DO UPDATE SET " - "name = EXCLUDED.name, configuration = EXCLUDED.configuration, revision = EXCLUDED.revision+1", - id, - OSUtils::jsonString(config["name"], ""), - OSUtils::jsonDump(config, -1), - _myAddressStr, - ((uint64_t)config["revision"])); - - w.commit(); - - const uint64_t nwidInt = OSUtils::jsonIntHex(config["nwid"], 0ULL); - if (nwidInt) { - nlohmann::json nwOrig; - nlohmann::json nwNew(config); - - get(nwidInt, nwOrig); - - _networkChanged(nwOrig, nwNew, qitem.second); - } - else { - fprintf(stderr, "%s: Can't notify network changed: %llu\n", _myAddressStr.c_str(), (unsigned long long)nwidInt); - } - } - catch (pqxx::data_exception& e) { - const pqxx::sql_error* s = dynamic_cast(&e); - fprintf(stderr, "%s ERROR: Error updating network: %s\n", _myAddressStr.c_str(), e.what()); - if (s) { - fprintf(stderr, "%s ERROR: SQL error: %s\n", _myAddressStr.c_str(), s->query().c_str()); - } - } - catch (std::exception& e) { - fprintf(stderr, "%s ERROR: Error updating network: %s\n", _myAddressStr.c_str(), e.what()); - } - } - else if (objtype == "_delete_network") { - // fprintf(stderr, "%s: commitThread: delete network\n", _myAddressStr.c_str()); - try { - // don't think we need this. Deletion handled by CV2 API - - pqxx::work w(*c->c); - std::string networkId = config["id"]; - - w.exec_params0("DELETE FROM network_memberships_ctl WHERE network_id = $1", networkId); - w.exec_params0("DELETE FROM networks_ctl WHERE id = $1", networkId); - - w.commit(); - } - catch (std::exception& e) { - fprintf(stderr, "%s ERROR: Error deleting network: %s\n", _myAddressStr.c_str(), e.what()); - } - } - else if (objtype == "_delete_member") { - // fprintf(stderr, "%s commitThread: delete member\n", _myAddressStr.c_str()); - try { - pqxx::work w(*c->c); - - std::string memberId = config["id"]; - std::string networkId = config["nwid"]; - - pqxx::result res = w.exec_params0("DELETE FROM network_memberships_ctl WHERE device_id = $1 AND network_id = $2", memberId, networkId); - - w.commit(); - } - catch (std::exception& e) { - fprintf(stderr, "%s ERROR: Error deleting member: %s\n", _myAddressStr.c_str(), e.what()); - } - } - else { - fprintf(stderr, "%s ERROR: unknown objtype\n", _myAddressStr.c_str()); - } - } - catch (std::exception& e) { - fprintf(stderr, "%s ERROR: Error getting objtype: %s\n", _myAddressStr.c_str(), e.what()); - } - _pool->unborrow(c); - c.reset(); - } - - fprintf(stderr, "%s commitThread finished\n", _myAddressStr.c_str()); -} - -void CV2::onlineNotificationThread() -{ - waitForReady(); - - _connected = 1; - - nlohmann::json jtmp1, jtmp2; - while (_run == 1) { - auto c = _pool->borrow(); - auto c2 = _pool->borrow(); - - try { - fprintf(stderr, "%s onlineNotificationThread\n", _myAddressStr.c_str()); - - std::unordered_map, NodeOnlineRecord, _PairHasher> lastOnline; - { - std::lock_guard l(_lastOnline_l); - lastOnline.swap(_lastOnline); - } - - pqxx::work w(*c->c); - pqxx::work w2(*c2->c); - - bool firstRun = true; - bool memberAdded = false; - uint64_t updateCount = 0; - - pqxx::pipeline pipe(w); - - for (auto i = lastOnline.begin(); i != lastOnline.end(); ++i) { - updateCount++; - - uint64_t nwid_i = i->first.first; - char nwidTmp[64]; - char memTmp[64]; - char ipTmp[64]; - - OSUtils::ztsnprintf(nwidTmp, sizeof(nwidTmp), "%.16llx", nwid_i); - OSUtils::ztsnprintf(memTmp, sizeof(memTmp), "%.10llx", i->first.second); - - if (! get(nwid_i, jtmp1, i->first.second, jtmp2)) { - continue; // skip non existent networks/members - } - - std::string networkId(nwidTmp); - std::string memberId(memTmp); - - try { - pqxx::row r = w2.exec_params1("SELECT device_id, network_id FROM network_memberships_ctl WHERE network_id = $1 AND device_id = $2", networkId, memberId); - } - catch (pqxx::unexpected_rows& e) { - continue; - } - - int64_t ts = i->second.lastSeen; - std::string ipAddr = i->second.physicalAddress.toIpString(ipTmp); - std::string timestamp = std::to_string(ts); - std::string osArch = i->second.osArch; - std::vector osArchSplit = split(osArch, '/'); - std::string os = osArchSplit[0]; - std::string arch = osArchSplit[1]; - - if (ipAddr.empty()) { - ipAddr = "relayed"; - } - - json record = { - { ipAddr, ts }, - }; - - std::string device_network_insert = "INSERT INTO network_memberships_ctl (device_id, network_id, last_seen, os, arch) " - "VALUES ('" - + w2.esc(memberId) + "', '" + w2.esc(networkId) + "', '" + w2.esc(record.dump()) - + "'::JSONB, " - "'" - + w2.esc(os) + "', '" + w2.esc(arch) - + "') " - "ON CONFLICT (device_id, network_id) DO UPDATE SET os = EXCLUDED.os, arch = EXCLUDED.arch, " - "last_seen = network_memberships_ctl.last_seen || EXCLUDED.last_seen"; - pipe.insert(device_network_insert); - - Metrics::pgsql_node_checkin++; - } - - pipe.complete(); - ; - w2.commit(); - w.commit(); - fprintf(stderr, "%s: Updated online status of %lu members\n", _myAddressStr.c_str(), updateCount); - } - catch (std::exception& e) { - fprintf(stderr, "%s ERROR: Error in onlineNotificationThread: %s\n", _myAddressStr.c_str(), e.what()); - } - catch (...) { - fprintf(stderr, "%s ERROR: Unknown error in onlineNotificationThread\n", _myAddressStr.c_str()); - } - _pool->unborrow(c2); - _pool->unborrow(c); - std::this_thread::sleep_for(std::chrono::seconds(10)); - } - - fprintf(stderr, "%s: Fell out of run loop in onlineNotificationThread\n", _myAddressStr.c_str()); - if (_run == 1) { - fprintf(stderr, "ERROR: %s onlineNotificationThread should still be running! Exiting Controller.\n", _myAddressStr.c_str()); - exit(6); - } -} -#endif // ZT_CONTROLLER_USE_LIBPQ \ No newline at end of file diff --git a/controller/CV2.hpp b/controller/CV2.hpp deleted file mode 100644 index 8302b98d3..000000000 --- a/controller/CV2.hpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c)2025 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#include "DB.hpp" - -#ifdef ZT_CONTROLLER_USE_LIBPQ - -#ifndef ZT_CONTROLLER_CV2_HPP -#define ZT_CONTROLLER_CV2_HPP - -#define ZT_CENTRAL_CONTROLLER_COMMIT_THREADS 4 - -#include "../node/Metrics.hpp" -#include "ConnectionPool.hpp" -#include "PostgreSQL.hpp" - -#include -#include -#include - -namespace ZeroTier { - -class CV2 : public DB { - public: - CV2(const Identity& myId, const char* path, int listenPort); - virtual ~CV2(); - - virtual bool waitForReady(); - virtual bool isReady(); - virtual bool save(nlohmann::json& record, bool notifyListeners); - virtual void eraseNetwork(const uint64_t networkId); - virtual void eraseMember(const uint64_t networkId, const uint64_t memberId); - virtual void nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress); - virtual void nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress, const char* osArch); - virtual AuthInfo getSSOAuthInfo(const nlohmann::json& member, const std::string& redirectURL); - - virtual bool ready() - { - return _ready == 2; - } - - protected: - struct _PairHasher { - inline std::size_t operator()(const std::pair& p) const - { - return (std::size_t)(p.first ^ p.second); - } - }; - virtual void _memberChanged(nlohmann::json& old, nlohmann::json& memberConfig, bool notifyListeners) - { - DB::_memberChanged(old, memberConfig, notifyListeners); - } - - virtual void _networkChanged(nlohmann::json& old, nlohmann::json& networkConfig, bool notifyListeners) - { - DB::_networkChanged(old, networkConfig, notifyListeners); - } - - private: - void initializeNetworks(); - void initializeMembers(); - void heartbeat(); - void membersDbWatcher(); - void networksDbWatcher(); - - void commitThread(); - void onlineNotificationThread(); - - // void notifyNewMember(const std::string &networkID, const std::string &memberID); - - enum OverrideMode { ALLOW_PGBOUNCER_OVERRIDE = 0, NO_OVERRIDE = 1 }; - - std::shared_ptr > _pool; - - const Identity _myId; - const Address _myAddress; - std::string _myAddressStr; - std::string _connString; - - BlockingQueue > _commitQueue; - - std::thread _heartbeatThread; - std::thread _membersDbWatcher; - std::thread _networksDbWatcher; - std::thread _commitThread[ZT_CENTRAL_CONTROLLER_COMMIT_THREADS]; - std::thread _onlineNotificationThread; - - std::unordered_map, NodeOnlineRecord, _PairHasher> _lastOnline; - - mutable std::mutex _lastOnline_l; - mutable std::mutex _readyLock; - std::atomic _ready, _connected, _run; - mutable volatile bool _waitNoticePrinted; - - int _listenPort; - uint8_t _ssoPsk[48]; -}; - -} // namespace ZeroTier - -#endif // ZT_CONTROLLER_CV2_HPP -#endif // ZT_CONTROLLER_USE_LIBPQ \ No newline at end of file diff --git a/controller/ConnectionPool.hpp b/controller/ConnectionPool.hpp deleted file mode 100644 index 3c080bad5..000000000 --- a/controller/ConnectionPool.hpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c)2021 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#ifndef ZT_CONNECTION_POOL_H_ -#define ZT_CONNECTION_POOL_H_ - -#ifndef _DEBUG -#define _DEBUG(x) -#endif - -#include "../node/Metrics.hpp" - -#include -#include -#include -#include -#include -#include - -namespace ZeroTier { - -struct ConnectionUnavailable : std::exception { - char const* what() const throw() - { - return "Unable to allocate connection"; - }; -}; - -class Connection { - public: - virtual ~Connection() {}; -}; - -class ConnectionFactory { - public: - virtual ~ConnectionFactory() {}; - virtual std::shared_ptr create() = 0; -}; - -struct ConnectionPoolStats { - size_t pool_size; - size_t borrowed_size; -}; - -template class ConnectionPool { - public: - ConnectionPool(size_t max_pool_size, size_t min_pool_size, std::shared_ptr factory) : m_maxPoolSize(max_pool_size), m_minPoolSize(min_pool_size), m_factory(factory) - { - Metrics::max_pool_size += max_pool_size; - Metrics::min_pool_size += min_pool_size; - while (m_pool.size() < m_minPoolSize) { - m_pool.push_back(m_factory->create()); - Metrics::pool_avail++; - } - }; - - ConnectionPoolStats get_stats() - { - std::unique_lock lock(m_poolMutex); - - ConnectionPoolStats stats; - stats.pool_size = m_pool.size(); - stats.borrowed_size = m_borrowed.size(); - - return stats; - }; - - ~ConnectionPool() {}; - - /** - * Borrow - * - * Borrow a connection for temporary use - * - * When done, either (a) call unborrow() to return it, or (b) (if it's bad) just let it go out of scope. This will cause it to automatically be replaced. - * @retval a shared_ptr to the connection object - */ - std::shared_ptr borrow() - { - std::unique_lock l(m_poolMutex); - - while ((m_pool.size() + m_borrowed.size()) < m_minPoolSize) { - std::shared_ptr conn = m_factory->create(); - m_pool.push_back(conn); - Metrics::pool_avail++; - } - - if (m_pool.size() == 0) { - if ((m_pool.size() + m_borrowed.size()) < m_maxPoolSize) { - try { - std::shared_ptr conn = m_factory->create(); - m_borrowed.insert(conn); - Metrics::pool_in_use++; - return std::static_pointer_cast(conn); - } - catch (std::exception& e) { - Metrics::pool_errors++; - throw ConnectionUnavailable(); - } - } - else { - for (auto it = m_borrowed.begin(); it != m_borrowed.end(); ++it) { - if ((*it).unique()) { - // This connection has been abandoned! Destroy it and create a new connection - try { - // If we are able to create a new connection, return it - _DEBUG("Creating new connection to replace discarded connection"); - std::shared_ptr conn = m_factory->create(); - m_borrowed.erase(it); - m_borrowed.insert(conn); - return std::static_pointer_cast(conn); - } - catch (std::exception& e) { - // Error creating a replacement connection - Metrics::pool_errors++; - throw ConnectionUnavailable(); - } - } - } - // Nothing available - Metrics::pool_errors++; - throw ConnectionUnavailable(); - } - } - - // Take one off the front - std::shared_ptr conn = m_pool.front(); - m_pool.pop_front(); - Metrics::pool_avail--; - // Add it to the borrowed list - m_borrowed.insert(conn); - Metrics::pool_in_use++; - return std::static_pointer_cast(conn); - }; - - /** - * Unborrow a connection - * - * Only call this if you are returning a working connection. If the connection was bad, just let it go out of scope (so the connection manager can replace it). - * @param the connection - */ - void unborrow(std::shared_ptr conn) - { - // Lock - std::unique_lock lock(m_poolMutex); - m_borrowed.erase(conn); - Metrics::pool_in_use--; - if ((m_pool.size() + m_borrowed.size()) < m_maxPoolSize) { - Metrics::pool_avail++; - m_pool.push_back(conn); - } - }; - - protected: - size_t m_maxPoolSize; - size_t m_minPoolSize; - std::shared_ptr m_factory; - std::deque > m_pool; - std::set > m_borrowed; - std::mutex m_poolMutex; -}; - -} // namespace ZeroTier - -#endif diff --git a/controller/CtlUtil.cpp b/controller/CtlUtil.cpp deleted file mode 100644 index e1e82f7a3..000000000 --- a/controller/CtlUtil.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "CtlUtil.hpp" - -#ifdef ZT_CONTROLLER_USE_LIBPQ - -#include -#include - -namespace ZeroTier { - -const char* _timestr() -{ - time_t t = time(0); - char* ts = ctime(&t); - char* p = ts; - if (! p) - return ""; - while (*p) { - if (*p == '\n') { - *p = (char)0; - break; - } - ++p; - } - return ts; -} - -std::vector split(std::string str, char delim) -{ - std::istringstream iss(str); - std::vector tokens; - std::string item; - while (std::getline(iss, item, delim)) { - tokens.push_back(item); - } - return tokens; -} - -std::string url_encode(const std::string& value) -{ - std::ostringstream escaped; - escaped.fill('0'); - escaped << std::hex; - - for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) { - std::string::value_type c = (*i); - - // Keep alphanumeric and other accepted characters intact - if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { - escaped << c; - continue; - } - - // Any other characters are percent-encoded - escaped << std::uppercase; - escaped << '%' << std::setw(2) << int((unsigned char)c); - escaped << std::nouppercase; - } - - return escaped.str(); -} - -} // namespace ZeroTier - -#endif \ No newline at end of file diff --git a/controller/CtlUtil.hpp b/controller/CtlUtil.hpp deleted file mode 100644 index 0ba665af3..000000000 --- a/controller/CtlUtil.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef ZT_CTLUTIL_HPP -#define ZT_CTLUTIL_HPP - -#include -#include - -namespace ZeroTier { - -const char* _timestr(); - -std::vector split(std::string str, char delim); - -std::string url_encode(const std::string& value); -} // namespace ZeroTier - -#endif // namespace ZeroTier \ No newline at end of file diff --git a/controller/DB.cpp b/controller/DB.cpp index 6d2f4f409..bb734dc85 100644 --- a/controller/DB.cpp +++ b/controller/DB.cpp @@ -1,125 +1,92 @@ /* - * Copyright (c)2019 ZeroTier, Inc. + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * Change Date: 2026-01-01 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ -/****/ #include "DB.hpp" - -#include "../node/Metrics.hpp" #include "EmbeddedNetworkController.hpp" -#include #include +#include #include using json = nlohmann::json; namespace ZeroTier { -void DB::initNetwork(nlohmann::json& network) +void DB::initNetwork(nlohmann::json &network) { - if (! network.count("private")) - network["private"] = true; - if (! network.count("creationTime")) - network["creationTime"] = OSUtils::now(); - if (! network.count("name")) - network["name"] = ""; - if (! network.count("multicastLimit")) - network["multicastLimit"] = (uint64_t)32; - if (! network.count("enableBroadcast")) - network["enableBroadcast"] = true; - if (! network.count("v4AssignMode")) - network["v4AssignMode"] = { { "zt", false } }; - if (! network.count("v6AssignMode")) - network["v6AssignMode"] = { { "rfc4193", false }, { "zt", false }, { "6plane", false } }; - if (! network.count("authTokens")) - network["authTokens"] = { {} }; - if (! network.count("capabilities")) - network["capabilities"] = nlohmann::json::array(); - if (! network.count("tags")) - network["tags"] = nlohmann::json::array(); - if (! network.count("routes")) - network["routes"] = nlohmann::json::array(); - if (! network.count("ipAssignmentPools")) - network["ipAssignmentPools"] = nlohmann::json::array(); - if (! network.count("mtu")) - network["mtu"] = ZT_DEFAULT_MTU; - if (! network.count("remoteTraceTarget")) - network["remoteTraceTarget"] = nlohmann::json(); - if (! network.count("removeTraceLevel")) - network["remoteTraceLevel"] = 0; - if (! network.count("rulesSource")) - network["rulesSource"] = ""; - if (! network.count("rules")) { + if (!network.count("private")) network["private"] = true; + if (!network.count("creationTime")) network["creationTime"] = OSUtils::now(); + if (!network.count("name")) network["name"] = ""; + if (!network.count("multicastLimit")) network["multicastLimit"] = (uint64_t)32; + if (!network.count("enableBroadcast")) network["enableBroadcast"] = true; + if (!network.count("v4AssignMode")) network["v4AssignMode"] = {{"zt",false}}; + if (!network.count("v6AssignMode")) network["v6AssignMode"] = {{"rfc4193",false},{"zt",false},{"6plane",false}}; + if (!network.count("authTokens")) network["authTokens"] = {{}}; + if (!network.count("capabilities")) network["capabilities"] = nlohmann::json::array(); + if (!network.count("tags")) network["tags"] = nlohmann::json::array(); + if (!network.count("routes")) network["routes"] = nlohmann::json::array(); + if (!network.count("ipAssignmentPools")) network["ipAssignmentPools"] = nlohmann::json::array(); + if (!network.count("mtu")) network["mtu"] = ZT_DEFAULT_MTU; + if (!network.count("remoteTraceTarget")) network["remoteTraceTarget"] = nlohmann::json(); + if (!network.count("removeTraceLevel")) network["remoteTraceLevel"] = 0; + if (!network.count("rules")) { // If unspecified, rules are set to allow anything and behave like a flat L2 segment - network["rules"] = { { { "not", false }, { "or", false }, { "type", "ACTION_ACCEPT" } } }; + network["rules"] = {{ + { "not",false }, + { "or", false }, + { "type","ACTION_ACCEPT" } + }}; } - if (! network.count("dns")) - network["dns"] = nlohmann::json::array(); - if (! network.count("ssoEnabled")) - network["ssoEnabled"] = false; - if (! network.count("clientId")) - network["clientId"] = ""; - if (! network.count("authorizationEndpoint")) - network["authorizationEndpoint"] = ""; - network["objtype"] = "network"; } -void DB::initMember(nlohmann::json& member) +void DB::initMember(nlohmann::json &member) { - if (! member.count("authorized")) - member["authorized"] = false; - if (! member.count("ssoExempt")) - member["ssoExempt"] = false; - if (! member.count("ipAssignments")) - member["ipAssignments"] = nlohmann::json::array(); - if (! member.count("activeBridge")) - member["activeBridge"] = false; - if (! member.count("tags")) - member["tags"] = nlohmann::json::array(); - if (! member.count("capabilities")) - member["capabilities"] = nlohmann::json::array(); - if (! member.count("creationTime")) - member["creationTime"] = OSUtils::now(); - if (! member.count("noAutoAssignIps")) - member["noAutoAssignIps"] = false; - if (! member.count("revision")) - member["revision"] = 0ULL; - if (! member.count("lastDeauthorizedTime")) - member["lastDeauthorizedTime"] = 0ULL; - if (! member.count("lastAuthorizedTime")) - member["lastAuthorizedTime"] = 0ULL; - if (! member.count("lastAuthorizedCredentialType")) - member["lastAuthorizedCredentialType"] = nlohmann::json(); - if (! member.count("lastAuthorizedCredential")) - member["lastAuthorizedCredential"] = nlohmann::json(); - if (! member.count("authenticationExpiryTime")) - member["authenticationExpiryTime"] = 0LL; - if (! member.count("vMajor")) - member["vMajor"] = -1; - if (! member.count("vMinor")) - member["vMinor"] = -1; - if (! member.count("vRev")) - member["vRev"] = -1; - if (! member.count("vProto")) - member["vProto"] = -1; - if (! member.count("remoteTraceTarget")) - member["remoteTraceTarget"] = nlohmann::json(); - if (! member.count("removeTraceLevel")) - member["remoteTraceLevel"] = 0; + if (!member.count("authorized")) member["authorized"] = false; + if (!member.count("ipAssignments")) member["ipAssignments"] = nlohmann::json::array(); + if (!member.count("activeBridge")) member["activeBridge"] = false; + if (!member.count("tags")) member["tags"] = nlohmann::json::array(); + if (!member.count("capabilities")) member["capabilities"] = nlohmann::json::array(); + if (!member.count("creationTime")) member["creationTime"] = OSUtils::now(); + if (!member.count("noAutoAssignIps")) member["noAutoAssignIps"] = false; + if (!member.count("revision")) member["revision"] = 0ULL; + if (!member.count("lastDeauthorizedTime")) member["lastDeauthorizedTime"] = 0ULL; + if (!member.count("lastAuthorizedTime")) member["lastAuthorizedTime"] = 0ULL; + if (!member.count("lastAuthorizedCredentialType")) member["lastAuthorizedCredentialType"] = nlohmann::json(); + if (!member.count("lastAuthorizedCredential")) member["lastAuthorizedCredential"] = nlohmann::json(); + if (!member.count("vMajor")) member["vMajor"] = -1; + if (!member.count("vMinor")) member["vMinor"] = -1; + if (!member.count("vRev")) member["vRev"] = -1; + if (!member.count("vProto")) member["vProto"] = -1; + if (!member.count("remoteTraceTarget")) member["remoteTraceTarget"] = nlohmann::json(); + if (!member.count("removeTraceLevel")) member["remoteTraceLevel"] = 0; member["objtype"] = "member"; } -void DB::cleanNetwork(nlohmann::json& network) +void DB::cleanNetwork(nlohmann::json &network) { network.erase("clock"); network.erase("authorizedMemberCount"); @@ -128,57 +95,58 @@ void DB::cleanNetwork(nlohmann::json& network) network.erase("lastModified"); } -void DB::cleanMember(nlohmann::json& member) +void DB::cleanMember(nlohmann::json &member) { member.erase("clock"); member.erase("physicalAddr"); member.erase("recentLog"); member.erase("lastModified"); member.erase("lastRequestMetaData"); - member.erase("authenticationURL"); // computed - member.erase("authenticationClientID"); // computed } -DB::DB() -{ -} -DB::~DB() +DB::DB(const Identity &myId,const char *path) : + _myId(myId), + _myAddress(myId.address()), + _path((path) ? path : "") { + char tmp[32]; + _myAddress.toString(tmp); + _myAddressStr = tmp; } -bool DB::get(const uint64_t networkId, nlohmann::json& network) +DB::~DB() {} + +bool DB::get(const uint64_t networkId,nlohmann::json &network) { waitForReady(); - Metrics::db_get_network++; std::shared_ptr<_Network> nw; { - std::shared_lock l(_networks_l); + std::lock_guard l(_networks_l); auto nwi = _networks.find(networkId); if (nwi == _networks.end()) return false; nw = nwi->second; } { - std::shared_lock l2(nw->lock); + std::lock_guard l2(nw->lock); network = nw->config; } return true; } -bool DB::get(const uint64_t networkId, nlohmann::json& network, const uint64_t memberId, nlohmann::json& member) +bool DB::get(const uint64_t networkId,nlohmann::json &network,const uint64_t memberId,nlohmann::json &member) { waitForReady(); - Metrics::db_get_network_and_member++; std::shared_ptr<_Network> nw; { - std::shared_lock l(_networks_l); + std::lock_guard l(_networks_l); auto nwi = _networks.find(networkId); if (nwi == _networks.end()) return false; nw = nwi->second; } { - std::shared_lock l2(nw->lock); + std::lock_guard l2(nw->lock); network = nw->config; auto m = nw->members.find(memberId); if (m == nw->members.end()) @@ -188,22 +156,21 @@ bool DB::get(const uint64_t networkId, nlohmann::json& network, const uint64_t m return true; } -bool DB::get(const uint64_t networkId, nlohmann::json& network, const uint64_t memberId, nlohmann::json& member, NetworkSummaryInfo& info) +bool DB::get(const uint64_t networkId,nlohmann::json &network,const uint64_t memberId,nlohmann::json &member,NetworkSummaryInfo &info) { waitForReady(); - Metrics::db_get_network_and_member_and_summary++; std::shared_ptr<_Network> nw; { - std::shared_lock l(_networks_l); + std::lock_guard l(_networks_l); auto nwi = _networks.find(networkId); if (nwi == _networks.end()) return false; nw = nwi->second; } { - std::shared_lock l2(nw->lock); + std::lock_guard l2(nw->lock); network = nw->config; - _fillSummaryInfo(nw, info); + _fillSummaryInfo(nw,info); auto m = nw->members.find(memberId); if (m == nw->members.end()) return false; @@ -212,40 +179,55 @@ bool DB::get(const uint64_t networkId, nlohmann::json& network, const uint64_t m return true; } -bool DB::get(const uint64_t networkId, nlohmann::json& network, std::vector& members) +bool DB::get(const uint64_t networkId,nlohmann::json &network,std::vector &members) { waitForReady(); - Metrics::db_get_member_list++; std::shared_ptr<_Network> nw; { - std::shared_lock l(_networks_l); + std::lock_guard l(_networks_l); auto nwi = _networks.find(networkId); if (nwi == _networks.end()) return false; nw = nwi->second; } { - std::shared_lock l2(nw->lock); + std::lock_guard l2(nw->lock); network = nw->config; - for (auto m = nw->members.begin(); m != nw->members.end(); ++m) { + for(auto m=nw->members.begin();m!=nw->members.end();++m) members.push_back(m->second); - } } return true; } -void DB::networks(std::set& networks) +bool DB::summary(const uint64_t networkId,NetworkSummaryInfo &info) { waitForReady(); - Metrics::db_get_network_list++; - std::shared_lock l(_networks_l); - for (auto n = _networks.begin(); n != _networks.end(); ++n) - networks.insert(n->first); + std::shared_ptr<_Network> nw; + { + std::lock_guard l(_networks_l); + auto nwi = _networks.find(networkId); + if (nwi == _networks.end()) + return false; + nw = nwi->second; + } + { + std::lock_guard l2(nw->lock); + _fillSummaryInfo(nw,info); + } + return true; } -void DB::_memberChanged(nlohmann::json& old, nlohmann::json& memberConfig, bool notifyListeners) +void DB::networks(std::vector &networks) +{ + waitForReady(); + std::lock_guard l(_networks_l); + networks.reserve(_networks.size() + 1); + for(auto n=_networks.begin();n!=_networks.end();++n) + networks.push_back(n->first); +} + +void DB::_memberChanged(nlohmann::json &old,nlohmann::json &memberConfig,bool initialized) { - Metrics::db_member_change++; uint64_t memberId = 0; uint64_t networkId = 0; bool isAuth = false; @@ -253,29 +235,26 @@ void DB::_memberChanged(nlohmann::json& old, nlohmann::json& memberConfig, bool std::shared_ptr<_Network> nw; if (old.is_object()) { - memberId = OSUtils::jsonIntHex(old["id"], 0ULL); - networkId = OSUtils::jsonIntHex(old["nwid"], 0ULL); - if ((memberId) && (networkId)) { + memberId = OSUtils::jsonIntHex(old["id"],0ULL); + networkId = OSUtils::jsonIntHex(old["nwid"],0ULL); + if ((memberId)&&(networkId)) { { - std::unique_lock l(_networks_l); + std::lock_guard l(_networks_l); auto nw2 = _networks.find(networkId); - if (nw2 != _networks.end()) { + if (nw2 != _networks.end()) nw = nw2->second; - } } if (nw) { - std::unique_lock l(nw->lock); - if (OSUtils::jsonBool(old["activeBridge"], false)) { + std::lock_guard l(nw->lock); + if (OSUtils::jsonBool(old["activeBridge"],false)) nw->activeBridgeMembers.erase(memberId); - } - wasAuth = OSUtils::jsonBool(old["authorized"], false); - if (wasAuth) { + wasAuth = OSUtils::jsonBool(old["authorized"],false); + if (wasAuth) nw->authorizedMembers.erase(memberId); - } - json& ips = old["ipAssignments"]; + json &ips = old["ipAssignments"]; if (ips.is_array()) { - for (unsigned long i = 0; i < ips.size(); ++i) { - json& ipj = ips[i]; + for(unsigned long i=0;i l(_networks_l); - std::shared_ptr<_Network>& nw2 = _networks[networkId]; - if (! nw2) + std::lock_guard l(_networks_l); + std::shared_ptr<_Network> &nw2 = _networks[networkId]; + if (!nw2) nw2.reset(new _Network); nw = nw2; } { - std::unique_lock l(nw->lock); + std::lock_guard l(nw->lock); nw->members[memberId] = memberConfig; - if (OSUtils::jsonBool(memberConfig["activeBridge"], false)) { + if (OSUtils::jsonBool(memberConfig["activeBridge"],false)) nw->activeBridgeMembers.insert(memberId); - } - isAuth = OSUtils::jsonBool(memberConfig["authorized"], false); - if (isAuth) { - Metrics::member_auths++; + isAuth = OSUtils::jsonBool(memberConfig["authorized"],false); + if (isAuth) nw->authorizedMembers.insert(memberId); - } - json& ips = memberConfig["ipAssignments"]; + json &ips = memberConfig["ipAssignments"]; if (ips.is_array()) { - for (unsigned long i = 0; i < ips.size(); ++i) { - json& ipj = ips[i]; + for(unsigned long i=0;i nw->mostRecentDeauthTime) nw->mostRecentDeauthTime = ldt; } } - if (notifyListeners) { - std::unique_lock ll(_changeListeners_l); - for (auto i = _changeListeners.begin(); i != _changeListeners.end(); ++i) { - (*i)->onNetworkMemberUpdate(this, networkId, memberId, memberConfig); + if (initialized) { + std::lock_guard ll(_changeListeners_l); + for(auto i=_changeListeners.begin();i!=_changeListeners.end();++i) { + (*i)->onNetworkMemberUpdate(networkId,memberId,memberConfig); } } - } - else if (memberId) { + } else if (memberId) { if (nw) { - std::unique_lock l(nw->lock); + std::lock_guard l(nw->lock); nw->members.erase(memberId); } if (networkId) { - std::unique_lock l(_networks_l); + std::lock_guard l(_networks_l); auto er = _networkByMember.equal_range(memberId); - for (auto i = er.first; i != er.second; ++i) { + for(auto i=er.first;i!=er.second;++i) { if (i->second == networkId) { _networkByMember.erase(i); break; @@ -358,114 +333,60 @@ void DB::_memberChanged(nlohmann::json& old, nlohmann::json& memberConfig, bool } } - if (notifyListeners) { - if (networkId != 0 && memberId != 0 && old.is_object() && ! memberConfig.is_object()) { - // member delete - Metrics::member_count--; - } - else if (networkId != 0 && memberId != 0 && ! old.is_object() && memberConfig.is_object()) { - // new member - Metrics::member_count++; - } - - if (! wasAuth && isAuth) { - Metrics::member_auths++; - } - else if (wasAuth && ! isAuth) { - Metrics::member_deauths++; - } - else { - Metrics::member_changes++; - } - } - - if ((notifyListeners) && ((wasAuth) && (! isAuth) && (networkId) && (memberId))) { - std::unique_lock ll(_changeListeners_l); - for (auto i = _changeListeners.begin(); i != _changeListeners.end(); ++i) { - (*i)->onNetworkMemberDeauthorize(this, networkId, memberId); + if ((initialized)&&((wasAuth)&&(!isAuth)&&(networkId)&&(memberId))) { + std::lock_guard ll(_changeListeners_l); + for(auto i=_changeListeners.begin();i!=_changeListeners.end();++i) { + (*i)->onNetworkMemberDeauthorize(networkId,memberId); } } } -void DB::_networkChanged(nlohmann::json& old, nlohmann::json& networkConfig, bool notifyListeners) +void DB::_networkChanged(nlohmann::json &old,nlohmann::json &networkConfig,bool initialized) { - Metrics::db_network_change++; - if (notifyListeners) { - if (old.is_object() && old.contains("id") && networkConfig.is_object() && networkConfig.contains("id")) { - Metrics::network_changes++; - } - else if (! old.is_object() && networkConfig.is_object() && networkConfig.contains("id")) { - Metrics::network_count++; - } - else if (old.is_object() && old.contains("id") && ! networkConfig.is_object()) { - Metrics::network_count--; - } - } - if (networkConfig.is_object()) { const std::string ids = networkConfig["id"]; const uint64_t networkId = Utils::hexStrToU64(ids.c_str()); if (networkId) { std::shared_ptr<_Network> nw; { - std::unique_lock l(_networks_l); - std::shared_ptr<_Network>& nw2 = _networks[networkId]; - if (! nw2) + std::lock_guard l(_networks_l); + std::shared_ptr<_Network> &nw2 = _networks[networkId]; + if (!nw2) nw2.reset(new _Network); nw = nw2; } { - std::unique_lock l2(nw->lock); + std::lock_guard l2(nw->lock); nw->config = networkConfig; } - if (notifyListeners) { - std::unique_lock ll(_changeListeners_l); - for (auto i = _changeListeners.begin(); i != _changeListeners.end(); ++i) { - (*i)->onNetworkUpdate(this, networkId, networkConfig); + if (initialized) { + std::lock_guard ll(_changeListeners_l); + for(auto i=_changeListeners.begin();i!=_changeListeners.end();++i) { + (*i)->onNetworkUpdate(networkId,networkConfig); } } } - } - else if (old.is_object()) { + } else if (old.is_object()) { const std::string ids = old["id"]; const uint64_t networkId = Utils::hexStrToU64(ids.c_str()); if (networkId) { - try { - // deauth all members on the network - nlohmann::json network; - std::vector members; - this->get(networkId, network, members); - for (auto i = members.begin(); i != members.end(); ++i) { - const std::string nodeID = (*i)["id"]; - const uint64_t memberId = Utils::hexStrToU64(nodeID.c_str()); - std::unique_lock ll(_changeListeners_l); - for (auto j = _changeListeners.begin(); j != _changeListeners.end(); ++j) { - (*j)->onNetworkMemberDeauthorize(this, networkId, memberId); - } - } - } - catch (std::exception& e) { - std::cerr << "Error deauthorizing members on network delete: " << e.what() << std::endl; - } - - // delete the network - std::unique_lock l(_networks_l); + std::lock_guard l(_networks_l); _networks.erase(networkId); } } } -void DB::_fillSummaryInfo(const std::shared_ptr<_Network>& nw, NetworkSummaryInfo& info) +void DB::_fillSummaryInfo(const std::shared_ptr<_Network> &nw,NetworkSummaryInfo &info) { - for (auto ab = nw->activeBridgeMembers.begin(); ab != nw->activeBridgeMembers.end(); ++ab) + for(auto ab=nw->activeBridgeMembers.begin();ab!=nw->activeBridgeMembers.end();++ab) info.activeBridges.push_back(Address(*ab)); - std::sort(info.activeBridges.begin(), info.activeBridges.end()); - for (auto ip = nw->allocatedIps.begin(); ip != nw->allocatedIps.end(); ++ip) + std::sort(info.activeBridges.begin(),info.activeBridges.end()); + for(auto ip=nw->allocatedIps.begin();ip!=nw->allocatedIps.end();++ip) info.allocatedIps.push_back(*ip); - std::sort(info.allocatedIps.begin(), info.allocatedIps.end()); + std::sort(info.allocatedIps.begin(),info.allocatedIps.end()); info.authorizedMemberCount = (unsigned long)nw->authorizedMembers.size(); info.totalMemberCount = (unsigned long)nw->members.size(); info.mostRecentDeauthTime = nw->mostRecentDeauthTime; } -} // namespace ZeroTier +} // namespace ZeroTier diff --git a/controller/DB.hpp b/controller/DB.hpp index 9dd9c06c6..f499d3874 100644 --- a/controller/DB.hpp +++ b/controller/DB.hpp @@ -1,94 +1,71 @@ /* - * Copyright (c)2019 ZeroTier, Inc. + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * Change Date: 2026-01-01 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ -/****/ #ifndef ZT_CONTROLLER_DB_HPP #define ZT_CONTROLLER_DB_HPP -// #define ZT_CONTROLLER_USE_LIBPQ - #include "../node/Constants.hpp" #include "../node/Identity.hpp" #include "../node/InetAddress.hpp" -#include "../osdep/BlockingQueue.hpp" #include "../osdep/OSUtils.hpp" +#include "../osdep/BlockingQueue.hpp" -#include -#include #include -#include -#include -#include -#include #include #include #include #include #include +#include +#include -#define ZT_MEMBER_AUTH_TIMEOUT_NOTIFY_BEFORE 25000 +#include "../ext/json/json.hpp" -namespace ZeroTier { - -struct AuthInfo { - public: - AuthInfo() : enabled(false), version(0), authenticationURL(), authenticationExpiryTime(0), issuerURL(), centralAuthURL(), ssoNonce(), ssoState(), ssoClientID(), ssoProvider("default") - { - } - - bool enabled; - uint64_t version; - std::string authenticationURL; - uint64_t authenticationExpiryTime; - std::string issuerURL; - std::string centralAuthURL; - std::string ssoNonce; - std::string ssoState; - std::string ssoClientID; - std::string ssoProvider; -}; +namespace ZeroTier +{ /** * Base class with common infrastructure for all controller DB implementations */ -class DB { -#ifdef ZT_CONTROLLER_USE_LIBPQ - friend class MemberNotificationReceiver; - friend class NetworkNotificationReceiver; -#endif - public: - class ChangeListener { - public: - ChangeListener() - { - } - virtual ~ChangeListener() - { - } - virtual void onNetworkUpdate(const void* db, uint64_t networkId, const nlohmann::json& network) - { - } - virtual void onNetworkMemberUpdate(const void* db, uint64_t networkId, uint64_t memberId, const nlohmann::json& member) - { - } - virtual void onNetworkMemberDeauthorize(const void* db, uint64_t networkId, uint64_t memberId) - { - } +class DB +{ +public: + class ChangeListener + { + public: + ChangeListener() {} + virtual ~ChangeListener() {} + virtual void onNetworkUpdate(uint64_t networkId,const nlohmann::json &network) {} + virtual void onNetworkMemberUpdate(uint64_t networkId,uint64_t memberId,const nlohmann::json &member) {} + virtual void onNetworkMemberDeauthorize(uint64_t networkId,uint64_t memberId) {} }; - struct NetworkSummaryInfo { - NetworkSummaryInfo() : authorizedMemberCount(0), totalMemberCount(0), mostRecentDeauthTime(0) - { - } + struct NetworkSummaryInfo + { + NetworkSummaryInfo() : authorizedMemberCount(0),totalMemberCount(0),mostRecentDeauthTime(0) {} std::vector
activeBridges; std::vector allocatedIps; unsigned long authorizedMemberCount; @@ -96,12 +73,12 @@ class DB { int64_t mostRecentDeauthTime; }; - static void initNetwork(nlohmann::json& network); - static void initMember(nlohmann::json& member); - static void cleanNetwork(nlohmann::json& network); - static void cleanMember(nlohmann::json& member); + static void initNetwork(nlohmann::json &network); + static void initMember(nlohmann::json &member); + static void cleanNetwork(nlohmann::json &network); + static void cleanMember(nlohmann::json &member); - DB(); + DB(const Identity &myId,const char *path); virtual ~DB(); virtual bool waitForReady() = 0; @@ -109,93 +86,57 @@ class DB { inline bool hasNetwork(const uint64_t networkId) const { - std::shared_lock l(_networks_l); + std::lock_guard l(_networks_l); return (_networks.find(networkId) != _networks.end()); } - bool get(const uint64_t networkId, nlohmann::json& network); - bool get(const uint64_t networkId, nlohmann::json& network, const uint64_t memberId, nlohmann::json& member); - bool get(const uint64_t networkId, nlohmann::json& network, const uint64_t memberId, nlohmann::json& member, NetworkSummaryInfo& info); - bool get(const uint64_t networkId, nlohmann::json& network, std::vector& members); + bool get(const uint64_t networkId,nlohmann::json &network); + bool get(const uint64_t networkId,nlohmann::json &network,const uint64_t memberId,nlohmann::json &member); + bool get(const uint64_t networkId,nlohmann::json &network,const uint64_t memberId,nlohmann::json &member,NetworkSummaryInfo &info); + bool get(const uint64_t networkId,nlohmann::json &network,std::vector &members); + bool summary(const uint64_t networkId,NetworkSummaryInfo &info); + void networks(std::vector &networks); - void networks(std::set& networks); - - template inline void each(F f) - { - nlohmann::json nullJson; - std::unique_lock lck(_networks_l); - for (auto nw = _networks.begin(); nw != _networks.end(); ++nw) { - f(nw->first, nw->second->config, 0, nullJson); // first provide network with 0 for member ID - for (auto m = nw->second->members.begin(); m != nw->second->members.end(); ++m) { - f(nw->first, nw->second->config, m->first, m->second); - } - } - } - - virtual bool save(nlohmann::json& record, bool notifyListeners) = 0; + virtual void save(nlohmann::json *orig,nlohmann::json &record) = 0; virtual void eraseNetwork(const uint64_t networkId) = 0; - virtual void eraseMember(const uint64_t networkId, const uint64_t memberId) = 0; - virtual void nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress) = 0; - virtual void nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress, const char* osArch) = 0; + virtual void eraseMember(const uint64_t networkId,const uint64_t memberId) = 0; + virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress) = 0; - virtual AuthInfo getSSOAuthInfo(const nlohmann::json& member, const std::string& redirectURL) + inline void addListener(DB::ChangeListener *const listener) { - return AuthInfo(); - } - - inline void addListener(DB::ChangeListener* const listener) - { - std::unique_lock l(_changeListeners_l); + std::lock_guard l(_changeListeners_l); _changeListeners.push_back(listener); } - protected: - static inline bool _compareRecords(const nlohmann::json& a, const nlohmann::json& b) +protected: + struct _Network { - if (a.is_object() == b.is_object()) { - if (a.is_object()) { - if (a.size() != b.size()) - return false; - auto amap = a.get(); - auto bmap = b.get(); - for (auto ai = amap.begin(); ai != amap.end(); ++ai) { - if (ai->first != "revision") { // ignore revision, compare only non-revision-counter fields - auto bi = bmap.find(ai->first); - if ((bi == bmap.end()) || (bi->second != ai->second)) - return false; - } - } - return true; - } - return (a == b); - } - return false; - } - - struct _Network { - _Network() : mostRecentDeauthTime(0) - { - } + _Network() : mostRecentDeauthTime(0) {} nlohmann::json config; - std::unordered_map members; + std::unordered_map members; std::unordered_set activeBridgeMembers; std::unordered_set authorizedMembers; - std::unordered_set allocatedIps; + std::unordered_set allocatedIps; int64_t mostRecentDeauthTime; - std::shared_mutex lock; + std::mutex lock; }; - virtual void _memberChanged(nlohmann::json& old, nlohmann::json& memberConfig, bool notifyListeners); - virtual void _networkChanged(nlohmann::json& old, nlohmann::json& networkConfig, bool notifyListeners); - void _fillSummaryInfo(const std::shared_ptr<_Network>& nw, NetworkSummaryInfo& info); + void _memberChanged(nlohmann::json &old,nlohmann::json &memberConfig,bool initialized); + void _networkChanged(nlohmann::json &old,nlohmann::json &networkConfig,bool initialized); + void _fillSummaryInfo(const std::shared_ptr<_Network> &nw,NetworkSummaryInfo &info); - std::vector _changeListeners; - std::unordered_map > _networks; - std::unordered_multimap _networkByMember; - mutable std::shared_mutex _changeListeners_l; - mutable std::shared_mutex _networks_l; + const Identity _myId; + const Address _myAddress; + const std::string _path; + std::string _myAddressStr; + + std::vector _changeListeners; + std::unordered_map< uint64_t,std::shared_ptr<_Network> > _networks; + std::unordered_multimap< uint64_t,uint64_t > _networkByMember; + mutable std::mutex _changeListeners_l; + mutable std::mutex _networks_l; }; -} // namespace ZeroTier +} // namespace ZeroTier #endif diff --git a/controller/DBMirrorSet.cpp b/controller/DBMirrorSet.cpp deleted file mode 100644 index bf2118923..000000000 --- a/controller/DBMirrorSet.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#include "DBMirrorSet.hpp" - -namespace ZeroTier { - -DBMirrorSet::DBMirrorSet(DB::ChangeListener* listener) : _listener(listener), _running(true), _syncCheckerThread(), _dbs(), _dbs_l() -{ - _syncCheckerThread = std::thread([this]() { - for (;;) { - for (int i = 0; i < 120; ++i) { // 1 minute delay between checks - if (! _running) - return; - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - - std::vector > dbs; - { - std::unique_lock l(_dbs_l); - if (_dbs.size() <= 1) - continue; // no need to do this if there's only one DB, so skip the iteration - dbs = _dbs; - } - - for (auto db = dbs.begin(); db != dbs.end(); ++db) { - (*db)->each([&dbs, &db](uint64_t networkId, const nlohmann::json& network, uint64_t memberId, const nlohmann::json& member) { - try { - if (network.is_object()) { - if (memberId == 0) { - for (auto db2 = dbs.begin(); db2 != dbs.end(); ++db2) { - if (db->get() != db2->get()) { - nlohmann::json nw2; - if ((! (*db2)->get(networkId, nw2)) || ((nw2.is_object()) && (OSUtils::jsonInt(nw2["revision"], 0) < OSUtils::jsonInt(network["revision"], 0)))) { - nw2 = network; - (*db2)->save(nw2, false); - } - } - } - } - else if (member.is_object()) { - for (auto db2 = dbs.begin(); db2 != dbs.end(); ++db2) { - if (db->get() != db2->get()) { - nlohmann::json nw2, m2; - if ((! (*db2)->get(networkId, nw2, memberId, m2)) || ((m2.is_object()) && (OSUtils::jsonInt(m2["revision"], 0) < OSUtils::jsonInt(member["revision"], 0)))) { - m2 = member; - (*db2)->save(m2, false); - } - } - } - } - } - } - catch (...) { - } // skip entries that generate JSON errors - }); - } - } - }); -} - -DBMirrorSet::~DBMirrorSet() -{ - _running = false; - _syncCheckerThread.join(); -} - -bool DBMirrorSet::hasNetwork(const uint64_t networkId) const -{ - std::shared_lock l(_dbs_l); - for (auto d = _dbs.begin(); d != _dbs.end(); ++d) { - if ((*d)->hasNetwork(networkId)) - return true; - } - return false; -} - -bool DBMirrorSet::get(const uint64_t networkId, nlohmann::json& network) -{ - std::shared_lock l(_dbs_l); - for (auto d = _dbs.begin(); d != _dbs.end(); ++d) { - if ((*d)->get(networkId, network)) { - return true; - } - } - return false; -} - -bool DBMirrorSet::get(const uint64_t networkId, nlohmann::json& network, const uint64_t memberId, nlohmann::json& member) -{ - std::shared_lock l(_dbs_l); - for (auto d = _dbs.begin(); d != _dbs.end(); ++d) { - if ((*d)->get(networkId, network, memberId, member)) - return true; - } - return false; -} - -bool DBMirrorSet::get(const uint64_t networkId, nlohmann::json& network, const uint64_t memberId, nlohmann::json& member, DB::NetworkSummaryInfo& info) -{ - std::shared_lock l(_dbs_l); - for (auto d = _dbs.begin(); d != _dbs.end(); ++d) { - if ((*d)->get(networkId, network, memberId, member, info)) - return true; - } - return false; -} - -bool DBMirrorSet::get(const uint64_t networkId, nlohmann::json& network, std::vector& members) -{ - std::shared_lock l(_dbs_l); - for (auto d = _dbs.begin(); d != _dbs.end(); ++d) { - if ((*d)->get(networkId, network, members)) - return true; - } - return false; -} - -AuthInfo DBMirrorSet::getSSOAuthInfo(const nlohmann::json& member, const std::string& redirectURL) -{ - std::shared_lock l(_dbs_l); - for (auto d = _dbs.begin(); d != _dbs.end(); ++d) { - AuthInfo info = (*d)->getSSOAuthInfo(member, redirectURL); - if (info.enabled) { - return info; - } - } - return AuthInfo(); -} - -void DBMirrorSet::networks(std::set& networks) -{ - std::shared_lock l(_dbs_l); - for (auto d = _dbs.begin(); d != _dbs.end(); ++d) { - (*d)->networks(networks); - } -} - -bool DBMirrorSet::waitForReady() -{ - bool r = false; - std::shared_lock l(_dbs_l); - for (auto d = _dbs.begin(); d != _dbs.end(); ++d) { - r |= (*d)->waitForReady(); - } - return r; -} - -bool DBMirrorSet::isReady() -{ - std::shared_lock l(_dbs_l); - for (auto d = _dbs.begin(); d != _dbs.end(); ++d) { - if (! (*d)->isReady()) - return false; - } - return true; -} - -bool DBMirrorSet::save(nlohmann::json& record, bool notifyListeners) -{ - std::vector > dbs; - { - std::unique_lock l(_dbs_l); - dbs = _dbs; - } - if (notifyListeners) { - for (auto d = dbs.begin(); d != dbs.end(); ++d) { - if ((*d)->save(record, true)) - return true; - } - return false; - } - else { - bool modified = false; - for (auto d = dbs.begin(); d != dbs.end(); ++d) { - modified |= (*d)->save(record, false); - } - return modified; - } -} - -void DBMirrorSet::eraseNetwork(const uint64_t networkId) -{ - std::unique_lock l(_dbs_l); - for (auto d = _dbs.begin(); d != _dbs.end(); ++d) { - (*d)->eraseNetwork(networkId); - } -} - -void DBMirrorSet::eraseMember(const uint64_t networkId, const uint64_t memberId) -{ - std::unique_lock l(_dbs_l); - for (auto d = _dbs.begin(); d != _dbs.end(); ++d) { - (*d)->eraseMember(networkId, memberId); - } -} - -void DBMirrorSet::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress, const char* osArch) -{ - std::shared_lock l(_dbs_l); - for (auto d = _dbs.begin(); d != _dbs.end(); ++d) { - (*d)->nodeIsOnline(networkId, memberId, physicalAddress, osArch); - } -} - -void DBMirrorSet::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress) -{ - this->nodeIsOnline(networkId, memberId, physicalAddress, "unknown/unknown"); -} - -void DBMirrorSet::onNetworkUpdate(const void* db, uint64_t networkId, const nlohmann::json& network) -{ - nlohmann::json record(network); - std::unique_lock l(_dbs_l); - for (auto d = _dbs.begin(); d != _dbs.end(); ++d) { - if (d->get() != db) { - (*d)->save(record, false); - } - } - _listener->onNetworkUpdate(this, networkId, network); -} - -void DBMirrorSet::onNetworkMemberUpdate(const void* db, uint64_t networkId, uint64_t memberId, const nlohmann::json& member) -{ - nlohmann::json record(member); - std::unique_lock l(_dbs_l); - for (auto d = _dbs.begin(); d != _dbs.end(); ++d) { - if (d->get() != db) { - (*d)->save(record, false); - } - } - _listener->onNetworkMemberUpdate(this, networkId, memberId, member); -} - -void DBMirrorSet::onNetworkMemberDeauthorize(const void* db, uint64_t networkId, uint64_t memberId) -{ - _listener->onNetworkMemberDeauthorize(this, networkId, memberId); -} - -} // namespace ZeroTier diff --git a/controller/DBMirrorSet.hpp b/controller/DBMirrorSet.hpp deleted file mode 100644 index 50230c545..000000000 --- a/controller/DBMirrorSet.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#ifndef ZT_DBMIRRORSET_HPP -#define ZT_DBMIRRORSET_HPP - -#include "DB.hpp" - -#include -#include -#include -#include -#include - -namespace ZeroTier { - -class DBMirrorSet : public DB::ChangeListener { - public: - DBMirrorSet(DB::ChangeListener* listener); - virtual ~DBMirrorSet(); - - bool hasNetwork(const uint64_t networkId) const; - - bool get(const uint64_t networkId, nlohmann::json& network); - bool get(const uint64_t networkId, nlohmann::json& network, const uint64_t memberId, nlohmann::json& member); - bool get(const uint64_t networkId, nlohmann::json& network, const uint64_t memberId, nlohmann::json& member, DB::NetworkSummaryInfo& info); - bool get(const uint64_t networkId, nlohmann::json& network, std::vector& members); - - void networks(std::set& networks); - - bool waitForReady(); - bool isReady(); - bool save(nlohmann::json& record, bool notifyListeners); - void eraseNetwork(const uint64_t networkId); - void eraseMember(const uint64_t networkId, const uint64_t memberId); - virtual void nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress); - virtual void nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress, const char* osArch); - - // These are called by various DB instances when changes occur. - virtual void onNetworkUpdate(const void* db, uint64_t networkId, const nlohmann::json& network); - virtual void onNetworkMemberUpdate(const void* db, uint64_t networkId, uint64_t memberId, const nlohmann::json& member); - virtual void onNetworkMemberDeauthorize(const void* db, uint64_t networkId, uint64_t memberId); - - AuthInfo getSSOAuthInfo(const nlohmann::json& member, const std::string& redirectURL); - - inline void addDB(const std::shared_ptr& db) - { - db->addListener(this); - std::unique_lock l(_dbs_l); - _dbs.push_back(db); - } - - private: - DB::ChangeListener* const _listener; - std::atomic_bool _running; - std::thread _syncCheckerThread; - std::vector > _dbs; - mutable std::shared_mutex _dbs_l; -}; - -} // namespace ZeroTier - -#endif diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 04f7ac6b2..bf5685271 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1,52 +1,62 @@ /* - * Copyright (c)2019 ZeroTier, Inc. + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * Change Date: 2026-01-01 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ -/****/ #include #include #include #include #include -#include #ifndef _WIN32 #include #endif -#include "../include/ZeroTierOne.h" -#include "../version.h" -#include "EmbeddedNetworkController.hpp" -#include "FileDB.hpp" -#include "LFDB.hpp" +#include #include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include + +#include "../include/ZeroTierOne.h" +#include "../version.h" + +#include "EmbeddedNetworkController.hpp" +#include "LFDB.hpp" +#include "FileDB.hpp" #ifdef ZT_CONTROLLER_USE_LIBPQ -#include "CV1.hpp" -#include "CV2.hpp" +#include "PostgreSQL.hpp" #endif +#include "../node/Node.hpp" #include "../node/CertificateOfMembership.hpp" +#include "../node/NetworkConfig.hpp" #include "../node/Dictionary.hpp" #include "../node/MAC.hpp" -#include "../node/NetworkConfig.hpp" -#include "../node/Node.hpp" using json = nlohmann::json; @@ -63,13 +73,13 @@ namespace ZeroTier { namespace { -static json _renderRule(ZT_VirtualNetworkRule& rule) +static json _renderRule(ZT_VirtualNetworkRule &rule) { char tmp[128]; json r = json::object(); const ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rule.t & 0x3f); - switch (rt) { + switch(rt) { case ZT_NETWORK_RULE_ACTION_DROP: r["type"] = "ACTION_DROP"; break; @@ -100,8 +110,8 @@ static json _renderRule(ZT_VirtualNetworkRule& rule) break; } - if (r.empty()) { - switch (rt) { + if (r.size() == 0) { + switch(rt) { case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS: r["type"] = "MATCH_SOURCE_ZEROTIER_ADDRESS"; r["zt"] = Address(rule.v.zt).toString(tmp); @@ -124,47 +134,29 @@ static json _renderRule(ZT_VirtualNetworkRule& rule) break; case ZT_NETWORK_RULE_MATCH_MAC_SOURCE: r["type"] = "MATCH_MAC_SOURCE"; - OSUtils::ztsnprintf( - tmp, - sizeof(tmp), - "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", - (unsigned int)rule.v.mac[0], - (unsigned int)rule.v.mac[1], - (unsigned int)rule.v.mac[2], - (unsigned int)rule.v.mac[3], - (unsigned int)rule.v.mac[4], - (unsigned int)rule.v.mac[5]); + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)rule.v.mac[0],(unsigned int)rule.v.mac[1],(unsigned int)rule.v.mac[2],(unsigned int)rule.v.mac[3],(unsigned int)rule.v.mac[4],(unsigned int)rule.v.mac[5]); r["mac"] = tmp; break; case ZT_NETWORK_RULE_MATCH_MAC_DEST: r["type"] = "MATCH_MAC_DEST"; - OSUtils::ztsnprintf( - tmp, - sizeof(tmp), - "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", - (unsigned int)rule.v.mac[0], - (unsigned int)rule.v.mac[1], - (unsigned int)rule.v.mac[2], - (unsigned int)rule.v.mac[3], - (unsigned int)rule.v.mac[4], - (unsigned int)rule.v.mac[5]); + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)rule.v.mac[0],(unsigned int)rule.v.mac[1],(unsigned int)rule.v.mac[2],(unsigned int)rule.v.mac[3],(unsigned int)rule.v.mac[4],(unsigned int)rule.v.mac[5]); r["mac"] = tmp; break; case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE: r["type"] = "MATCH_IPV4_SOURCE"; - r["ip"] = InetAddress(&(rule.v.ipv4.ip), 4, (unsigned int)rule.v.ipv4.mask).toString(tmp); + r["ip"] = InetAddress(&(rule.v.ipv4.ip),4,(unsigned int)rule.v.ipv4.mask).toString(tmp); break; case ZT_NETWORK_RULE_MATCH_IPV4_DEST: r["type"] = "MATCH_IPV4_DEST"; - r["ip"] = InetAddress(&(rule.v.ipv4.ip), 4, (unsigned int)rule.v.ipv4.mask).toString(tmp); + r["ip"] = InetAddress(&(rule.v.ipv4.ip),4,(unsigned int)rule.v.ipv4.mask).toString(tmp); break; case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE: r["type"] = "MATCH_IPV6_SOURCE"; - r["ip"] = InetAddress(rule.v.ipv6.ip, 16, (unsigned int)rule.v.ipv6.mask).toString(tmp); + r["ip"] = InetAddress(rule.v.ipv6.ip,16,(unsigned int)rule.v.ipv6.mask).toString(tmp); break; case ZT_NETWORK_RULE_MATCH_IPV6_DEST: r["type"] = "MATCH_IPV6_DEST"; - r["ip"] = InetAddress(rule.v.ipv6.ip, 16, (unsigned int)rule.v.ipv6.mask).toString(tmp); + r["ip"] = InetAddress(rule.v.ipv6.ip,16,(unsigned int)rule.v.ipv6.mask).toString(tmp); break; case ZT_NETWORK_RULE_MATCH_IP_TOS: r["type"] = "MATCH_IP_TOS"; @@ -185,8 +177,7 @@ static json _renderRule(ZT_VirtualNetworkRule& rule) r["icmpType"] = (unsigned int)rule.v.icmp.type; if ((rule.v.icmp.flags & 0x01) != 0) r["icmpCode"] = (unsigned int)rule.v.icmp.code; - else - r["icmpCode"] = json(); + else r["icmpCode"] = json(); break; case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE: r["type"] = "MATCH_IP_SOURCE_PORT_RANGE"; @@ -200,7 +191,7 @@ static json _renderRule(ZT_VirtualNetworkRule& rule) break; case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS: r["type"] = "MATCH_CHARACTERISTICS"; - OSUtils::ztsnprintf(tmp, sizeof(tmp), "%.16llx", rule.v.characteristics); + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",rule.v.characteristics); r["mask"] = tmp; break; case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE: @@ -249,9 +240,9 @@ static json _renderRule(ZT_VirtualNetworkRule& rule) break; case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE: r["type"] = "INTEGER_RANGE"; - OSUtils::ztsnprintf(tmp, sizeof(tmp), "%.16llx", rule.v.intRange.start); + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",rule.v.intRange.start); r["start"] = tmp; - OSUtils::ztsnprintf(tmp, sizeof(tmp), "%.16llx", rule.v.intRange.start + (uint64_t)rule.v.intRange.end); + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",rule.v.intRange.start + (uint64_t)rule.v.intRange.end); r["end"] = tmp; r["idx"] = rule.v.intRange.idx; r["little"] = ((rule.v.intRange.format & 0x80) != 0); @@ -261,7 +252,7 @@ static json _renderRule(ZT_VirtualNetworkRule& rule) break; } - if (! r.empty()) { + if (r.size() > 0) { r["not"] = ((rule.t & 0x80) != 0); r["or"] = ((rule.t & 0x40) != 0); } @@ -270,303 +261,221 @@ static json _renderRule(ZT_VirtualNetworkRule& rule) return r; } -static bool _parseRule(json& r, ZT_VirtualNetworkRule& rule) +static bool _parseRule(json &r,ZT_VirtualNetworkRule &rule) { - if (! r.is_object()) + if (!r.is_object()) return false; - const std::string t(OSUtils::jsonString(r["type"], "")); - memset(&rule, 0, sizeof(ZT_VirtualNetworkRule)); + const std::string t(OSUtils::jsonString(r["type"],"")); + memset(&rule,0,sizeof(ZT_VirtualNetworkRule)); - if (OSUtils::jsonBool(r["not"], false)) + if (OSUtils::jsonBool(r["not"],false)) rule.t = 0x80; - else - rule.t = 0x00; - if (OSUtils::jsonBool(r["or"], false)) + else rule.t = 0x00; + if (OSUtils::jsonBool(r["or"],false)) rule.t |= 0x40; bool tag = false; if (t == "ACTION_DROP") { rule.t |= ZT_NETWORK_RULE_ACTION_DROP; return true; - } - else if (t == "ACTION_ACCEPT") { + } else if (t == "ACTION_ACCEPT") { rule.t |= ZT_NETWORK_RULE_ACTION_ACCEPT; return true; - } - else if (t == "ACTION_TEE") { + } else if (t == "ACTION_TEE") { rule.t |= ZT_NETWORK_RULE_ACTION_TEE; - rule.v.fwd.address = Utils::hexStrToU64(OSUtils::jsonString(r["address"], "0").c_str()) & 0xffffffffffULL; - rule.v.fwd.flags = (uint32_t)(OSUtils::jsonInt(r["flags"], 0ULL) & 0xffffffffULL); - rule.v.fwd.length = (uint16_t)(OSUtils::jsonInt(r["length"], 0ULL) & 0xffffULL); + rule.v.fwd.address = Utils::hexStrToU64(OSUtils::jsonString(r["address"],"0").c_str()) & 0xffffffffffULL; + rule.v.fwd.flags = (uint32_t)(OSUtils::jsonInt(r["flags"],0ULL) & 0xffffffffULL); + rule.v.fwd.length = (uint16_t)(OSUtils::jsonInt(r["length"],0ULL) & 0xffffULL); return true; - } - else if (t == "ACTION_WATCH") { + } else if (t == "ACTION_WATCH") { rule.t |= ZT_NETWORK_RULE_ACTION_WATCH; - rule.v.fwd.address = Utils::hexStrToU64(OSUtils::jsonString(r["address"], "0").c_str()) & 0xffffffffffULL; - rule.v.fwd.flags = (uint32_t)(OSUtils::jsonInt(r["flags"], 0ULL) & 0xffffffffULL); - rule.v.fwd.length = (uint16_t)(OSUtils::jsonInt(r["length"], 0ULL) & 0xffffULL); + rule.v.fwd.address = Utils::hexStrToU64(OSUtils::jsonString(r["address"],"0").c_str()) & 0xffffffffffULL; + rule.v.fwd.flags = (uint32_t)(OSUtils::jsonInt(r["flags"],0ULL) & 0xffffffffULL); + rule.v.fwd.length = (uint16_t)(OSUtils::jsonInt(r["length"],0ULL) & 0xffffULL); return true; - } - else if (t == "ACTION_REDIRECT") { + } else if (t == "ACTION_REDIRECT") { rule.t |= ZT_NETWORK_RULE_ACTION_REDIRECT; - rule.v.fwd.address = Utils::hexStrToU64(OSUtils::jsonString(r["address"], "0").c_str()) & 0xffffffffffULL; - rule.v.fwd.flags = (uint32_t)(OSUtils::jsonInt(r["flags"], 0ULL) & 0xffffffffULL); + rule.v.fwd.address = Utils::hexStrToU64(OSUtils::jsonString(r["address"],"0").c_str()) & 0xffffffffffULL; + rule.v.fwd.flags = (uint32_t)(OSUtils::jsonInt(r["flags"],0ULL) & 0xffffffffULL); return true; - } - else if (t == "ACTION_BREAK") { + } else if (t == "ACTION_BREAK") { rule.t |= ZT_NETWORK_RULE_ACTION_BREAK; return true; - } - else if (t == "MATCH_SOURCE_ZEROTIER_ADDRESS") { + } else if (t == "MATCH_SOURCE_ZEROTIER_ADDRESS") { rule.t |= ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS; - rule.v.zt = Utils::hexStrToU64(OSUtils::jsonString(r["zt"], "0").c_str()) & 0xffffffffffULL; + rule.v.zt = Utils::hexStrToU64(OSUtils::jsonString(r["zt"],"0").c_str()) & 0xffffffffffULL; return true; - } - else if (t == "MATCH_DEST_ZEROTIER_ADDRESS") { + } else if (t == "MATCH_DEST_ZEROTIER_ADDRESS") { rule.t |= ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS; - rule.v.zt = Utils::hexStrToU64(OSUtils::jsonString(r["zt"], "0").c_str()) & 0xffffffffffULL; + rule.v.zt = Utils::hexStrToU64(OSUtils::jsonString(r["zt"],"0").c_str()) & 0xffffffffffULL; return true; - } - else if (t == "MATCH_VLAN_ID") { + } else if (t == "MATCH_VLAN_ID") { rule.t |= ZT_NETWORK_RULE_MATCH_VLAN_ID; - rule.v.vlanId = (uint16_t)(OSUtils::jsonInt(r["vlanId"], 0ULL) & 0xffffULL); + rule.v.vlanId = (uint16_t)(OSUtils::jsonInt(r["vlanId"],0ULL) & 0xffffULL); return true; - } - else if (t == "MATCH_VLAN_PCP") { + } else if (t == "MATCH_VLAN_PCP") { rule.t |= ZT_NETWORK_RULE_MATCH_VLAN_PCP; - rule.v.vlanPcp = (uint8_t)(OSUtils::jsonInt(r["vlanPcp"], 0ULL) & 0xffULL); + rule.v.vlanPcp = (uint8_t)(OSUtils::jsonInt(r["vlanPcp"],0ULL) & 0xffULL); return true; - } - else if (t == "MATCH_VLAN_DEI") { + } else if (t == "MATCH_VLAN_DEI") { rule.t |= ZT_NETWORK_RULE_MATCH_VLAN_DEI; - rule.v.vlanDei = (uint8_t)(OSUtils::jsonInt(r["vlanDei"], 0ULL) & 0xffULL); + rule.v.vlanDei = (uint8_t)(OSUtils::jsonInt(r["vlanDei"],0ULL) & 0xffULL); return true; - } - else if (t == "MATCH_MAC_SOURCE") { + } else if (t == "MATCH_MAC_SOURCE") { rule.t |= ZT_NETWORK_RULE_MATCH_MAC_SOURCE; - std::string mac(OSUtils::jsonString(r["mac"], "0")); - Utils::cleanMac(mac); - Utils::unhex(mac.c_str(), (unsigned int)mac.length(), rule.v.mac, 6); + const std::string mac(OSUtils::jsonString(r["mac"],"0")); + Utils::unhex(mac.c_str(),(unsigned int)mac.length(),rule.v.mac,6); return true; - } - else if (t == "MATCH_MAC_DEST") { + } else if (t == "MATCH_MAC_DEST") { rule.t |= ZT_NETWORK_RULE_MATCH_MAC_DEST; - std::string mac(OSUtils::jsonString(r["mac"], "0")); - Utils::cleanMac(mac); - Utils::unhex(mac.c_str(), (unsigned int)mac.length(), rule.v.mac, 6); + const std::string mac(OSUtils::jsonString(r["mac"],"0")); + Utils::unhex(mac.c_str(),(unsigned int)mac.length(),rule.v.mac,6); return true; - } - else if (t == "MATCH_IPV4_SOURCE") { + } else if (t == "MATCH_IPV4_SOURCE") { rule.t |= ZT_NETWORK_RULE_MATCH_IPV4_SOURCE; - InetAddress ip(OSUtils::jsonString(r["ip"], "0.0.0.0").c_str()); - rule.v.ipv4.ip = reinterpret_cast(&ip)->sin_addr.s_addr; - rule.v.ipv4.mask = Utils::ntoh(reinterpret_cast(&ip)->sin_port) & 0xff; - if (rule.v.ipv4.mask > 32) - rule.v.ipv4.mask = 32; + InetAddress ip(OSUtils::jsonString(r["ip"],"0.0.0.0").c_str()); + rule.v.ipv4.ip = reinterpret_cast(&ip)->sin_addr.s_addr; + rule.v.ipv4.mask = Utils::ntoh(reinterpret_cast(&ip)->sin_port) & 0xff; + if (rule.v.ipv4.mask > 32) rule.v.ipv4.mask = 32; return true; - } - else if (t == "MATCH_IPV4_DEST") { + } else if (t == "MATCH_IPV4_DEST") { rule.t |= ZT_NETWORK_RULE_MATCH_IPV4_DEST; - InetAddress ip(OSUtils::jsonString(r["ip"], "0.0.0.0").c_str()); - rule.v.ipv4.ip = reinterpret_cast(&ip)->sin_addr.s_addr; - rule.v.ipv4.mask = Utils::ntoh(reinterpret_cast(&ip)->sin_port) & 0xff; - if (rule.v.ipv4.mask > 32) - rule.v.ipv4.mask = 32; + InetAddress ip(OSUtils::jsonString(r["ip"],"0.0.0.0").c_str()); + rule.v.ipv4.ip = reinterpret_cast(&ip)->sin_addr.s_addr; + rule.v.ipv4.mask = Utils::ntoh(reinterpret_cast(&ip)->sin_port) & 0xff; + if (rule.v.ipv4.mask > 32) rule.v.ipv4.mask = 32; return true; - } - else if (t == "MATCH_IPV6_SOURCE") { + } else if (t == "MATCH_IPV6_SOURCE") { rule.t |= ZT_NETWORK_RULE_MATCH_IPV6_SOURCE; - InetAddress ip(OSUtils::jsonString(r["ip"], "::0").c_str()); - memcpy(rule.v.ipv6.ip, reinterpret_cast(&ip)->sin6_addr.s6_addr, 16); - rule.v.ipv6.mask = Utils::ntoh(reinterpret_cast(&ip)->sin6_port) & 0xff; - if (rule.v.ipv6.mask > 128) - rule.v.ipv6.mask = 128; + InetAddress ip(OSUtils::jsonString(r["ip"],"::0").c_str()); + memcpy(rule.v.ipv6.ip,reinterpret_cast(&ip)->sin6_addr.s6_addr,16); + rule.v.ipv6.mask = Utils::ntoh(reinterpret_cast(&ip)->sin6_port) & 0xff; + if (rule.v.ipv6.mask > 128) rule.v.ipv6.mask = 128; return true; - } - else if (t == "MATCH_IPV6_DEST") { + } else if (t == "MATCH_IPV6_DEST") { rule.t |= ZT_NETWORK_RULE_MATCH_IPV6_DEST; - InetAddress ip(OSUtils::jsonString(r["ip"], "::0").c_str()); - memcpy(rule.v.ipv6.ip, reinterpret_cast(&ip)->sin6_addr.s6_addr, 16); - rule.v.ipv6.mask = Utils::ntoh(reinterpret_cast(&ip)->sin6_port) & 0xff; - if (rule.v.ipv6.mask > 128) - rule.v.ipv6.mask = 128; + InetAddress ip(OSUtils::jsonString(r["ip"],"::0").c_str()); + memcpy(rule.v.ipv6.ip,reinterpret_cast(&ip)->sin6_addr.s6_addr,16); + rule.v.ipv6.mask = Utils::ntoh(reinterpret_cast(&ip)->sin6_port) & 0xff; + if (rule.v.ipv6.mask > 128) rule.v.ipv6.mask = 128; return true; - } - else if (t == "MATCH_IP_TOS") { + } else if (t == "MATCH_IP_TOS") { rule.t |= ZT_NETWORK_RULE_MATCH_IP_TOS; - rule.v.ipTos.mask = (uint8_t)(OSUtils::jsonInt(r["mask"], 0ULL) & 0xffULL); - rule.v.ipTos.value[0] = (uint8_t)(OSUtils::jsonInt(r["start"], 0ULL) & 0xffULL); - rule.v.ipTos.value[1] = (uint8_t)(OSUtils::jsonInt(r["end"], 0ULL) & 0xffULL); + rule.v.ipTos.mask = (uint8_t)(OSUtils::jsonInt(r["mask"],0ULL) & 0xffULL); + rule.v.ipTos.value[0] = (uint8_t)(OSUtils::jsonInt(r["start"],0ULL) & 0xffULL); + rule.v.ipTos.value[1] = (uint8_t)(OSUtils::jsonInt(r["end"],0ULL) & 0xffULL); return true; - } - else if (t == "MATCH_IP_PROTOCOL") { + } else if (t == "MATCH_IP_PROTOCOL") { rule.t |= ZT_NETWORK_RULE_MATCH_IP_PROTOCOL; - rule.v.ipProtocol = (uint8_t)(OSUtils::jsonInt(r["ipProtocol"], 0ULL) & 0xffULL); + rule.v.ipProtocol = (uint8_t)(OSUtils::jsonInt(r["ipProtocol"],0ULL) & 0xffULL); return true; - } - else if (t == "MATCH_ETHERTYPE") { + } else if (t == "MATCH_ETHERTYPE") { rule.t |= ZT_NETWORK_RULE_MATCH_ETHERTYPE; - rule.v.etherType = (uint16_t)(OSUtils::jsonInt(r["etherType"], 0ULL) & 0xffffULL); + rule.v.etherType = (uint16_t)(OSUtils::jsonInt(r["etherType"],0ULL) & 0xffffULL); return true; - } - else if (t == "MATCH_ICMP") { + } else if (t == "MATCH_ICMP") { rule.t |= ZT_NETWORK_RULE_MATCH_ICMP; - rule.v.icmp.type = (uint8_t)(OSUtils::jsonInt(r["icmpType"], 0ULL) & 0xffULL); - json& code = r["icmpCode"]; + rule.v.icmp.type = (uint8_t)(OSUtils::jsonInt(r["icmpType"],0ULL) & 0xffULL); + json &code = r["icmpCode"]; if (code.is_null()) { rule.v.icmp.code = 0; rule.v.icmp.flags = 0x00; - } - else { - rule.v.icmp.code = (uint8_t)(OSUtils::jsonInt(code, 0ULL) & 0xffULL); + } else { + rule.v.icmp.code = (uint8_t)(OSUtils::jsonInt(code,0ULL) & 0xffULL); rule.v.icmp.flags = 0x01; } return true; - } - else if (t == "MATCH_IP_SOURCE_PORT_RANGE") { + } else if (t == "MATCH_IP_SOURCE_PORT_RANGE") { rule.t |= ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE; - rule.v.port[0] = (uint16_t)(OSUtils::jsonInt(r["start"], 0ULL) & 0xffffULL); - rule.v.port[1] = (uint16_t)(OSUtils::jsonInt(r["end"], (uint64_t)rule.v.port[0]) & 0xffffULL); + rule.v.port[0] = (uint16_t)(OSUtils::jsonInt(r["start"],0ULL) & 0xffffULL); + rule.v.port[1] = (uint16_t)(OSUtils::jsonInt(r["end"],(uint64_t)rule.v.port[0]) & 0xffffULL); return true; - } - else if (t == "MATCH_IP_DEST_PORT_RANGE") { + } else if (t == "MATCH_IP_DEST_PORT_RANGE") { rule.t |= ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE; - rule.v.port[0] = (uint16_t)(OSUtils::jsonInt(r["start"], 0ULL) & 0xffffULL); - rule.v.port[1] = (uint16_t)(OSUtils::jsonInt(r["end"], (uint64_t)rule.v.port[0]) & 0xffffULL); + rule.v.port[0] = (uint16_t)(OSUtils::jsonInt(r["start"],0ULL) & 0xffffULL); + rule.v.port[1] = (uint16_t)(OSUtils::jsonInt(r["end"],(uint64_t)rule.v.port[0]) & 0xffffULL); return true; - } - else if (t == "MATCH_CHARACTERISTICS") { + } else if (t == "MATCH_CHARACTERISTICS") { rule.t |= ZT_NETWORK_RULE_MATCH_CHARACTERISTICS; if (r.count("mask")) { - json& v = r["mask"]; + json &v = r["mask"]; if (v.is_number()) { rule.v.characteristics = v; - } - else { + } else { std::string tmp = v; rule.v.characteristics = Utils::hexStrToU64(tmp.c_str()); } } return true; - } - else if (t == "MATCH_FRAME_SIZE_RANGE") { + } else if (t == "MATCH_FRAME_SIZE_RANGE") { rule.t |= ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE; - rule.v.frameSize[0] = (uint16_t)(OSUtils::jsonInt(r["start"], 0ULL) & 0xffffULL); - rule.v.frameSize[1] = (uint16_t)(OSUtils::jsonInt(r["end"], (uint64_t)rule.v.frameSize[0]) & 0xffffULL); + rule.v.frameSize[0] = (uint16_t)(OSUtils::jsonInt(r["start"],0ULL) & 0xffffULL); + rule.v.frameSize[1] = (uint16_t)(OSUtils::jsonInt(r["end"],(uint64_t)rule.v.frameSize[0]) & 0xffffULL); return true; - } - else if (t == "MATCH_RANDOM") { + } else if (t == "MATCH_RANDOM") { rule.t |= ZT_NETWORK_RULE_MATCH_RANDOM; - rule.v.randomProbability = (uint32_t)(OSUtils::jsonInt(r["probability"], 0ULL) & 0xffffffffULL); + rule.v.randomProbability = (uint32_t)(OSUtils::jsonInt(r["probability"],0ULL) & 0xffffffffULL); return true; - } - else if (t == "MATCH_TAGS_DIFFERENCE") { + } else if (t == "MATCH_TAGS_DIFFERENCE") { rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE; tag = true; - } - else if (t == "MATCH_TAGS_BITWISE_AND") { + } else if (t == "MATCH_TAGS_BITWISE_AND") { rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND; tag = true; - } - else if (t == "MATCH_TAGS_BITWISE_OR") { + } else if (t == "MATCH_TAGS_BITWISE_OR") { rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR; tag = true; - } - else if (t == "MATCH_TAGS_BITWISE_XOR") { + } else if (t == "MATCH_TAGS_BITWISE_XOR") { rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR; tag = true; - } - else if (t == "MATCH_TAGS_EQUAL") { + } else if (t == "MATCH_TAGS_EQUAL") { rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_EQUAL; tag = true; - } - else if (t == "MATCH_TAG_SENDER") { + } else if (t == "MATCH_TAG_SENDER") { rule.t |= ZT_NETWORK_RULE_MATCH_TAG_SENDER; tag = true; - } - else if (t == "MATCH_TAG_RECEIVER") { + } else if (t == "MATCH_TAG_RECEIVER") { rule.t |= ZT_NETWORK_RULE_MATCH_TAG_RECEIVER; tag = true; - } - else if (t == "INTEGER_RANGE") { - json& s = r["start"]; + } else if (t == "INTEGER_RANGE") { + json &s = r["start"]; if (s.is_string()) { std::string tmp = s; rule.v.intRange.start = Utils::hexStrToU64(tmp.c_str()); + } else { + rule.v.intRange.start = OSUtils::jsonInt(s,0ULL); } - else { - rule.v.intRange.start = OSUtils::jsonInt(s, 0ULL); - } - json& e = r["end"]; + json &e = r["end"]; if (e.is_string()) { std::string tmp = e; rule.v.intRange.end = (uint32_t)(Utils::hexStrToU64(tmp.c_str()) - rule.v.intRange.start); + } else { + rule.v.intRange.end = (uint32_t)(OSUtils::jsonInt(e,0ULL) - rule.v.intRange.start); } - else { - rule.v.intRange.end = (uint32_t)(OSUtils::jsonInt(e, 0ULL) - rule.v.intRange.start); - } - rule.v.intRange.idx = (uint16_t)OSUtils::jsonInt(r["idx"], 0ULL); - rule.v.intRange.format = (OSUtils::jsonBool(r["little"], false)) ? 0x80 : 0x00; - rule.v.intRange.format |= (uint8_t)((OSUtils::jsonInt(r["bits"], 1ULL) - 1) & 63); + rule.v.intRange.idx = (uint16_t)OSUtils::jsonInt(r["idx"],0ULL); + rule.v.intRange.format = (OSUtils::jsonBool(r["little"],false)) ? 0x80 : 0x00; + rule.v.intRange.format |= (uint8_t)((OSUtils::jsonInt(r["bits"],1ULL) - 1) & 63); } if (tag) { - rule.v.tag.id = (uint32_t)(OSUtils::jsonInt(r["id"], 0ULL) & 0xffffffffULL); - rule.v.tag.value = (uint32_t)(OSUtils::jsonInt(r["value"], 0ULL) & 0xffffffffULL); + rule.v.tag.id = (uint32_t)(OSUtils::jsonInt(r["id"],0ULL) & 0xffffffffULL); + rule.v.tag.value = (uint32_t)(OSUtils::jsonInt(r["value"],0ULL) & 0xffffffffULL); return true; } return false; } -} // anonymous namespace +} // anonymous namespace -EmbeddedNetworkController::EmbeddedNetworkController(Node* node, const char* ztPath, const char* dbPath, int listenPort, RedisConfig* rc) - : _startTime(OSUtils::now()) - , _listenPort(listenPort) - , _node(node) - , _ztPath(ztPath) - , _path(dbPath) - , _signingId() - , _signingIdAddressString() - , _sender((NetworkController::Sender*)0) - , _db(this) - , _queue() - , _threads() - , _threads_l() - , _memberStatus() - , _memberStatus_l() - , _expiringSoon() - , _expiringSoon_l() - , _rc(rc) - , _ssoExpiryRunning(true) - , _ssoExpiry(std::thread(&EmbeddedNetworkController::_ssoExpiryThread, this)) - -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - , _member_status_lookup { "nc_member_status_lookup", "" } - , _member_status_lookup_count { "nc_member_status_lookup_count", "" } - , _node_is_online { "nc_node_is_online", "" } - , _node_is_online_count { "nc_node_is_online_count", "" } - , _get_and_init_member { "nc_get_and_init_member", "" } - , _get_and_init_member_count { "nc_get_and_init_member_count", "" } - , _have_identity { "nc_have_identity", "" } - , _have_identity_count { "nc_have_identity_count", "" } - , _determine_auth { "nc_determine_auth", "" } - , _determine_auth_count { "nc_determine_auth_count", "" } - , _sso_check { "nc_sso_check", "" } - , _sso_check_count { "nc_sso_check_count", "" } - , _auth_check { "nc_auth_check", "" } - , _auth_check_count { "nc_auth_check_count", "" } - , _json_schlep { "nc_json_schlep", "" } - , _json_schlep_count { "nc_json_schlep_count", "" } - , _issue_certificate { "nc_issue_certificate", "" } - , _issue_certificate_count { "nc_issue_certificate_count", "" } - , _save_member { "nc_save_member", "" } - , _save_member_count { "nc_save_member_count", "" } - , _send_netconf { "nc_send_netconf2", "" } - , _send_netconf_count { "nc_send_netconf2_count", "" } -#endif +EmbeddedNetworkController::EmbeddedNetworkController(Node *node,const char *dbPath, int listenPort, MQConfig *mqc) : + _startTime(OSUtils::now()), + _listenPort(listenPort), + _node(node), + _path(dbPath), + _sender((NetworkController::Sender *)0), + _mqc(mqc) { } @@ -574,19 +483,11 @@ EmbeddedNetworkController::~EmbeddedNetworkController() { std::lock_guard l(_threads_l); _queue.stop(); - for (auto t = _threads.begin(); t != _threads.end(); ++t) { + for(auto t=_threads.begin();t!=_threads.end();++t) t->join(); - } - _ssoExpiryRunning = false; - _ssoExpiry.join(); } -void EmbeddedNetworkController::setSSORedirectURL(const std::string& url) -{ - _ssoRedirectURL = url; -} - -void EmbeddedNetworkController::init(const Identity& signingId, Sender* sender) +void EmbeddedNetworkController::init(const Identity &signingId,Sender *sender) { char tmp[64]; _signingId = signingId; @@ -594,29 +495,18 @@ void EmbeddedNetworkController::init(const Identity& signingId, Sender* sender) _signingIdAddressString = signingId.address().toString(tmp); #ifdef ZT_CONTROLLER_USE_LIBPQ - if ((_path.length() > 9) && (_path.substr(0, 9) == "postgres:")) { - fprintf(stderr, "CV1\n"); - _db.addDB(std::shared_ptr(new CV1(_signingId, _path.substr(9).c_str(), _listenPort, _rc))); - } - else if ((_path.length() > 4) && (_path.substr(0, 4) == "cv2:")) { - fprintf(stderr, "CV2\n"); - _db.addDB(std::shared_ptr(new CV2(_signingId, _path.substr(4).c_str(), _listenPort))); - } - else { - fprintf(stderr, "FileDB\n"); -#endif - _db.addDB(std::shared_ptr(new FileDB(_path.c_str()))); -#ifdef ZT_CONTROLLER_USE_LIBPQ - } + if ((_path.length() > 9)&&(_path.substr(0,9) == "postgres:")) { + _db.reset(new PostgreSQL(_signingId,_path.substr(9).c_str(), _listenPort, _mqc)); + } else { #endif std::string lfJSON; - OSUtils::readFile((_ztPath + ZT_PATH_SEPARATOR_S "local.conf").c_str(), lfJSON); + OSUtils::readFile((_path + ZT_PATH_SEPARATOR_S ".." ZT_PATH_SEPARATOR_S "local.conf").c_str(),lfJSON); if (lfJSON.length() > 0) { nlohmann::json lfConfig(OSUtils::jsonParse(lfJSON)); - nlohmann::json& settings = lfConfig["settings"]; + nlohmann::json &settings = lfConfig["settings"]; if (settings.is_object()) { - nlohmann::json& controllerDb = settings["controllerDb"]; + nlohmann::json &controllerDb = settings["controllerDb"]; if (controllerDb.is_object()) { std::string type = controllerDb["type"]; if (type == "lf") { @@ -624,14 +514,14 @@ void EmbeddedNetworkController::init(const Identity& signingId, Sender* sender) std::string lfHost = controllerDb["host"]; int lfPort = controllerDb["port"]; bool storeOnlineState = controllerDb["storeOnlineState"]; - if ((lfOwner.length()) && (lfHost.length()) && (lfPort > 0) && (lfPort < 65536)) { + if ((lfOwner.length())&&(lfHost.length())&&(lfPort > 0)&&(lfPort < 65536)) { std::size_t pubHdrLoc = lfOwner.find("Public: "); - if ((pubHdrLoc > 0) && ((pubHdrLoc + 8) < lfOwner.length())) { + if ((pubHdrLoc > 0)&&((pubHdrLoc + 8) < lfOwner.length())) { std::string lfOwnerPublic = lfOwner.substr(pubHdrLoc + 8); std::size_t pubHdrEnd = lfOwnerPublic.find_first_of("\n\r\t "); if (pubHdrEnd != std::string::npos) { - lfOwnerPublic = lfOwnerPublic.substr(0, pubHdrEnd); - _db.addDB(std::shared_ptr(new LFDB(_signingId, _path.c_str(), lfOwner.c_str(), lfOwnerPublic.c_str(), lfHost.c_str(), lfPort, storeOnlineState))); + lfOwnerPublic = lfOwnerPublic.substr(0,pubHdrEnd); + _db.reset(new LFDB(_signingId,_path.c_str(),lfOwner.c_str(),lfOwnerPublic.c_str(),lfHost.c_str(),lfPort,storeOnlineState)); } } } @@ -639,28 +529,29 @@ void EmbeddedNetworkController::init(const Identity& signingId, Sender* sender) } } } + if (!_db) + _db.reset(new FileDB(_signingId,_path.c_str())); - _db.waitForReady(); + _db->addListener(this); + +#ifdef ZT_CONTROLLER_USE_LIBPQ + } +#endif + + _db->waitForReady(); } -void EmbeddedNetworkController::request(uint64_t nwid, const InetAddress& fromAddr, uint64_t requestPacketId, const Identity& identity, const Dictionary& metaData) +void EmbeddedNetworkController::request( + uint64_t nwid, + const InetAddress &fromAddr, + uint64_t requestPacketId, + const Identity &identity, + const Dictionary &metaData) { - if (((! _signingId) || (! _signingId.hasPrivate())) || (_signingId.address().toInt() != (nwid >> 24)) || (! _sender)) + if (((!_signingId)||(!_signingId.hasPrivate()))||(_signingId.address().toInt() != (nwid >> 24))||(!_sender)) return; _startThreads(); - - const int64_t now = OSUtils::now(); - - if (requestPacketId) { - std::lock_guard l(_memberStatus_l); - _MemberStatus& ms = _memberStatus[_MemberStatusKey(nwid, identity.address().toInt())]; - if ((now - ms.lastRequestTime) <= ZT_NETCONF_MIN_REQUEST_PERIOD) { - return; - } - ms.lastRequestTime = now; - } - - _RQEntry* qe = new _RQEntry; + _RQEntry *qe = new _RQEntry; qe->nwid = nwid; qe->requestPacketId = requestPacketId; qe->fromAddr = fromAddr; @@ -670,905 +561,730 @@ void EmbeddedNetworkController::request(uint64_t nwid, const InetAddress& fromAd _queue.post(qe); } -std::string EmbeddedNetworkController::networkUpdateFromPostData(uint64_t networkID, const std::string& body) +unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET( + const std::vector &path, + const std::map &urlArgs, + const std::map &headers, + const std::string &body, + std::string &responseBody, + std::string &responseContentType) { - json b = OSUtils::jsonParse(body); + if (!_db) + return 500; - char nwids[24]; - OSUtils::ztsnprintf(nwids, sizeof(nwids), "%.16llx", networkID); + if ((path.size() > 0)&&(path[0] == "network")) { - json network; - _db.get(networkID, network); - DB::initNetwork(network); - if (b.count("name")) - network["name"] = OSUtils::jsonString(b["name"], ""); - if (b.count("private")) - network["private"] = OSUtils::jsonBool(b["private"], true); - if (b.count("enableBroadcast")) - network["enableBroadcast"] = OSUtils::jsonBool(b["enableBroadcast"], false); - if (b.count("multicastLimit")) - network["multicastLimit"] = OSUtils::jsonInt(b["multicastLimit"], 32ULL); - if (b.count("mtu")) - network["mtu"] = std::max(std::min((unsigned int)OSUtils::jsonInt(b["mtu"], ZT_DEFAULT_MTU), (unsigned int)ZT_MAX_MTU), (unsigned int)ZT_MIN_MTU); + if ((path.size() >= 2)&&(path[1].length() == 16)) { + const uint64_t nwid = Utils::hexStrToU64(path[1].c_str()); + json network; + if (!_db->get(nwid,network)) + return 404; - if (b.count("remoteTraceTarget")) { - const std::string rtt(OSUtils::jsonString(b["remoteTraceTarget"], "")); - if (rtt.length() == 10) { - network["remoteTraceTarget"] = rtt; - } - else { - network["remoteTraceTarget"] = json(); - } - } - if (b.count("remoteTraceLevel")) - network["remoteTraceLevel"] = OSUtils::jsonInt(b["remoteTraceLevel"], 0ULL); + if (path.size() >= 3) { - if (b.count("v4AssignMode")) { - json nv4m; - json& v4m = b["v4AssignMode"]; - if (v4m.is_string()) { // backward compatibility - nv4m["zt"] = (OSUtils::jsonString(v4m, "") == "zt"); - } - else if (v4m.is_object()) { - nv4m["zt"] = OSUtils::jsonBool(v4m["zt"], false); - } - else - nv4m["zt"] = false; - network["v4AssignMode"] = nv4m; - } + if (path[2] == "member") { - if (b.count("v6AssignMode")) { - json nv6m; - json& v6m = b["v6AssignMode"]; - if (! nv6m.is_object()) - nv6m = json::object(); - if (v6m.is_string()) { // backward compatibility - std::vector v6ms(OSUtils::split(OSUtils::jsonString(v6m, "").c_str(), ",", "", "")); - std::sort(v6ms.begin(), v6ms.end()); - v6ms.erase(std::unique(v6ms.begin(), v6ms.end()), v6ms.end()); - nv6m["rfc4193"] = false; - nv6m["zt"] = false; - nv6m["6plane"] = false; - for (std::vector::iterator i(v6ms.begin()); i != v6ms.end(); ++i) { - if (*i == "rfc4193") - nv6m["rfc4193"] = true; - else if (*i == "zt") - nv6m["zt"] = true; - else if (*i == "6plane") - nv6m["6plane"] = true; - } - } - else if (v6m.is_object()) { - if (v6m.count("rfc4193")) - nv6m["rfc4193"] = OSUtils::jsonBool(v6m["rfc4193"], false); - if (v6m.count("zt")) - nv6m["zt"] = OSUtils::jsonBool(v6m["zt"], false); - if (v6m.count("6plane")) - nv6m["6plane"] = OSUtils::jsonBool(v6m["6plane"], false); - } - else { - nv6m["rfc4193"] = false; - nv6m["zt"] = false; - nv6m["6plane"] = false; - } - network["v6AssignMode"] = nv6m; - } + if (path.size() >= 4) { + // Get member - if (b.count("routes")) { - json& rts = b["routes"]; - if (rts.is_array()) { - json nrts = json::array(); - for (unsigned long i = 0; i < rts.size(); ++i) { - json& rt = rts[i]; - if (rt.is_object()) { - json& target = rt["target"]; - json& via = rt["via"]; - if (target.is_string()) { - InetAddress t(target.get().c_str()); - InetAddress v; - if (via.is_string()) - v.fromString(via.get().c_str()); - if (((t.ss_family == AF_INET) || (t.ss_family == AF_INET6)) && (t.netmaskBitsValid())) { - json tmp; - char tmp2[64]; - tmp["target"] = t.toString(tmp2); - if (v.ss_family == t.ss_family) - tmp["via"] = v.toIpString(tmp2); - else - tmp["via"] = json(); - nrts.push_back(tmp); - if (nrts.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) - break; + const uint64_t address = Utils::hexStrToU64(path[3].c_str()); + json member; + if (!_db->get(nwid,network,address,member)) + return 404; + responseBody = OSUtils::jsonDump(member); + responseContentType = "application/json"; + + } else { + // List members and their revisions + + responseBody = "{"; + std::vector members; + if (_db->get(nwid,network,members)) { + responseBody.reserve((members.size() + 2) * 32); + std::string mid; + for(auto member=members.begin();member!=members.end();++member) { + mid = (*member)["id"]; + char tmp[128]; + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s\"%s\":%llu",(responseBody.length() > 1) ? "," : "",mid.c_str(),(unsigned long long)OSUtils::jsonInt((*member)["revision"],0)); + responseBody.append(tmp); + } } + responseBody.push_back('}'); + responseContentType = "application/json"; + } - } + return 200; + + } // else 404 + + } else { + // Get network + + responseBody = OSUtils::jsonDump(network); + responseContentType = "application/json"; + return 200; + } - network["routes"] = nrts; - } + } else if (path.size() == 1) { + // List networks + + std::vector networkIds; + _db->networks(networkIds); + char tmp[64]; + responseBody = "["; + responseBody.reserve((networkIds.size() + 1) * 24); + for(std::vector::const_iterator i(networkIds.begin());i!=networkIds.end();++i) { + if (responseBody.length() > 1) + responseBody.push_back(','); + OSUtils::ztsnprintf(tmp,sizeof(tmp),"\"%.16llx\"",(unsigned long long)*i); + responseBody.append(tmp); + } + responseBody.push_back(']'); + responseContentType = "application/json"; + + return 200; + + } // else 404 + + } else { + // Controller status + + char tmp[4096]; + const bool dbOk = _db->isReady(); + OSUtils::ztsnprintf(tmp,sizeof(tmp),"{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu,\n\t\"databaseReady\": %s\n}\n",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now(),dbOk ? "true" : "false"); + responseBody = tmp; + responseContentType = "application/json"; + return dbOk ? 200 : 503; + } - if (b.count("ipAssignmentPools")) { - json& ipp = b["ipAssignmentPools"]; - if (ipp.is_array()) { - json nipp = json::array(); - for (unsigned long i = 0; i < ipp.size(); ++i) { - json& ip = ipp[i]; - if ((ip.is_object()) && (ip.count("ipRangeStart")) && (ip.count("ipRangeEnd"))) { - InetAddress f(OSUtils::jsonString(ip["ipRangeStart"], "").c_str()); - InetAddress t(OSUtils::jsonString(ip["ipRangeEnd"], "").c_str()); - if (((f.ss_family == AF_INET) || (f.ss_family == AF_INET6)) && (f.ss_family == t.ss_family)) { - json tmp = json::object(); - char tmp2[64]; - tmp["ipRangeStart"] = f.toIpString(tmp2); - tmp["ipRangeEnd"] = t.toIpString(tmp2); - nipp.push_back(tmp); - if (nipp.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) - break; - } - } - } - network["ipAssignmentPools"] = nipp; + return 404; +} + +unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( + const std::vector &path, + const std::map &urlArgs, + const std::map &headers, + const std::string &body, + std::string &responseBody, + std::string &responseContentType) +{ + if (!_db) + return 500; + if (path.empty()) + return 404; + + json b; + try { + b = OSUtils::jsonParse(body); + if (!b.is_object()) { + responseBody = "{ \"message\": \"body is not a JSON object\" }"; + responseContentType = "application/json"; + return 400; } + } catch ( ... ) { + responseBody = "{ \"message\": \"body JSON is invalid\" }"; + responseContentType = "application/json"; + return 400; } + const int64_t now = OSUtils::now(); - if (b.count("rules")) { - json& rules = b["rules"]; - if (rules.is_array()) { - json nrules = json::array(); - for (unsigned long i = 0; i < rules.size(); ++i) { - json& rule = rules[i]; - if (rule.is_object()) { - ZT_VirtualNetworkRule ztr; - if (_parseRule(rule, ztr)) { - nrules.push_back(_renderRule(ztr)); - if (nrules.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) - break; - } - } - } - network["rules"] = nrules; - } - } + if (path[0] == "network") { - if (b.count("authTokens")) { - json& authTokens = b["authTokens"]; - if (authTokens.is_object()) { - json nat; - for (json::iterator t(authTokens.begin()); t != authTokens.end(); ++t) { - if ((t.value().is_number()) && (t.value() >= 0)) - nat[t.key()] = t.value(); - } - network["authTokens"] = nat; - } - else { - network["authTokens"] = { {} }; - } - } + if ((path.size() >= 2)&&(path[1].length() == 16)) { + uint64_t nwid = Utils::hexStrToU64(path[1].c_str()); + char nwids[24]; + OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); - if (b.count("capabilities")) { - json& capabilities = b["capabilities"]; - if (capabilities.is_array()) { - std::map ncaps; - for (unsigned long i = 0; i < capabilities.size(); ++i) { - json& cap = capabilities[i]; - if (cap.is_object()) { - json ncap = json::object(); - const uint64_t capId = OSUtils::jsonInt(cap["id"], 0ULL); - ncap["id"] = capId; - ncap["default"] = OSUtils::jsonBool(cap["default"], false); + if (path.size() >= 3) { - json& rules = cap["rules"]; - json nrules = json::array(); - if (rules.is_array()) { - for (unsigned long i = 0; i < rules.size(); ++i) { - json& rule = rules[i]; - if (rule.is_object()) { - ZT_VirtualNetworkRule ztr; - if (_parseRule(rule, ztr)) { - nrules.push_back(_renderRule(ztr)); - if (nrules.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) - break; + if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) { + uint64_t address = Utils::hexStrToU64(path[3].c_str()); + char addrs[24]; + OSUtils::ztsnprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)address); + + json member,network; + _db->get(nwid,network,address,member); + json origMember(member); // for detecting changes + DB::initMember(member); + + try { + if (b.count("activeBridge")) member["activeBridge"] = OSUtils::jsonBool(b["activeBridge"],false); + if (b.count("noAutoAssignIps")) member["noAutoAssignIps"] = OSUtils::jsonBool(b["noAutoAssignIps"],false); + + if (b.count("remoteTraceTarget")) { + const std::string rtt(OSUtils::jsonString(b["remoteTraceTarget"],"")); + if (rtt.length() == 10) { + member["remoteTraceTarget"] = rtt; + } else { + member["remoteTraceTarget"] = json(); + } + } + if (b.count("remoteTraceLevel")) member["remoteTraceLevel"] = OSUtils::jsonInt(b["remoteTraceLevel"],0ULL); + + if (b.count("authorized")) { + const bool newAuth = OSUtils::jsonBool(b["authorized"],false); + if (newAuth != OSUtils::jsonBool(member["authorized"],false)) { + member["authorized"] = newAuth; + member[((newAuth) ? "lastAuthorizedTime" : "lastDeauthorizedTime")] = now; + if (newAuth) { + member["lastAuthorizedCredentialType"] = "api"; + member["lastAuthorizedCredential"] = json(); } } } + + if (b.count("ipAssignments")) { + json &ipa = b["ipAssignments"]; + if (ipa.is_array()) { + json mipa(json::array()); + for(unsigned long i=0;i= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + } + member["ipAssignments"] = mipa; + } + } + + if (b.count("tags")) { + json &tags = b["tags"]; + if (tags.is_array()) { + std::map mtags; + for(unsigned long i=0;i::iterator t(mtags.begin());t!=mtags.end();++t) { + json ta = json::array(); + ta.push_back(t->first); + ta.push_back(t->second); + mtagsa.push_back(ta); + if (mtagsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + member["tags"] = mtagsa; + } + } + + if (b.count("capabilities")) { + json &capabilities = b["capabilities"]; + if (capabilities.is_array()) { + json mcaps = json::array(); + for(unsigned long i=0;i= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + std::sort(mcaps.begin(),mcaps.end()); + mcaps.erase(std::unique(mcaps.begin(),mcaps.end()),mcaps.end()); + member["capabilities"] = mcaps; + } + } + } catch ( ... ) { + responseBody = "{ \"message\": \"exception while processing parameters in JSON body\" }"; + responseContentType = "application/json"; + return 400; } - ncap["rules"] = nrules; - ncaps[capId] = ncap; + member["id"] = addrs; + member["address"] = addrs; // legacy + member["nwid"] = nwids; + + DB::cleanMember(member); + _db->save(&origMember,member); + responseBody = OSUtils::jsonDump(member); + responseContentType = "application/json"; + + return 200; + } // else 404 + + } else { + // POST to network ID + + // Magic ID ending with ______ picks a random unused network ID + if (path[1].substr(10) == "______") { + nwid = 0; + uint64_t nwidPrefix = (Utils::hexStrToU64(path[1].substr(0,10).c_str()) << 24) & 0xffffffffff000000ULL; + uint64_t nwidPostfix = 0; + for(unsigned long k=0;k<100000;++k) { // sanity limit on trials + Utils::getSecureRandom(&nwidPostfix,sizeof(nwidPostfix)); + uint64_t tryNwid = nwidPrefix | (nwidPostfix & 0xffffffULL); + if ((tryNwid & 0xffffffULL) == 0ULL) tryNwid |= 1ULL; + if (!_db->hasNetwork(tryNwid)) { + nwid = tryNwid; + break; + } + } + if (!nwid) + return 503; } - } + OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); - json ncapsa = json::array(); - for (std::map::iterator c(ncaps.begin()); c != ncaps.end(); ++c) { - ncapsa.push_back(c->second); - if (ncapsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) - break; - } - network["capabilities"] = ncapsa; - } - } + json network; + _db->get(nwid,network); + json origNetwork(network); // for detecting changes + DB::initNetwork(network); - if (b.count("tags")) { - json& tags = b["tags"]; - if (tags.is_array()) { - std::map ntags; - for (unsigned long i = 0; i < tags.size(); ++i) { - json& tag = tags[i]; - if (tag.is_object()) { - json ntag = json::object(); - const uint64_t tagId = OSUtils::jsonInt(tag["id"], 0ULL); - ntag["id"] = tagId; - json& dfl = tag["default"]; - if (dfl.is_null()) - ntag["default"] = dfl; - else - ntag["default"] = OSUtils::jsonInt(dfl, 0ULL); - ntags[tagId] = ntag; - } - } + try { + if (b.count("name")) network["name"] = OSUtils::jsonString(b["name"],""); + if (b.count("private")) network["private"] = OSUtils::jsonBool(b["private"],true); + if (b.count("enableBroadcast")) network["enableBroadcast"] = OSUtils::jsonBool(b["enableBroadcast"],false); + if (b.count("multicastLimit")) network["multicastLimit"] = OSUtils::jsonInt(b["multicastLimit"],32ULL); + if (b.count("mtu")) network["mtu"] = std::max(std::min((unsigned int)OSUtils::jsonInt(b["mtu"],ZT_DEFAULT_MTU),(unsigned int)ZT_MAX_MTU),(unsigned int)ZT_MIN_MTU); - json ntagsa = json::array(); - for (std::map::iterator t(ntags.begin()); t != ntags.end(); ++t) { - ntagsa.push_back(t->second); - if (ntagsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) - break; - } - network["tags"] = ntagsa; - } - } + if (b.count("remoteTraceTarget")) { + const std::string rtt(OSUtils::jsonString(b["remoteTraceTarget"],"")); + if (rtt.length() == 10) { + network["remoteTraceTarget"] = rtt; + } else { + network["remoteTraceTarget"] = json(); + } + } + if (b.count("remoteTraceLevel")) network["remoteTraceLevel"] = OSUtils::jsonInt(b["remoteTraceLevel"],0ULL); - if (b.count("dns")) { - json& dns = b["dns"]; - if (dns.is_object()) { - json nd; + if (b.count("v4AssignMode")) { + json nv4m; + json &v4m = b["v4AssignMode"]; + if (v4m.is_string()) { // backward compatibility + nv4m["zt"] = (OSUtils::jsonString(v4m,"") == "zt"); + } else if (v4m.is_object()) { + nv4m["zt"] = OSUtils::jsonBool(v4m["zt"],false); + } else nv4m["zt"] = false; + network["v4AssignMode"] = nv4m; + } - nd["domain"] = dns["domain"]; + if (b.count("v6AssignMode")) { + json nv6m; + json &v6m = b["v6AssignMode"]; + if (!nv6m.is_object()) nv6m = json::object(); + if (v6m.is_string()) { // backward compatibility + std::vector v6ms(OSUtils::split(OSUtils::jsonString(v6m,"").c_str(),",","","")); + std::sort(v6ms.begin(),v6ms.end()); + v6ms.erase(std::unique(v6ms.begin(),v6ms.end()),v6ms.end()); + nv6m["rfc4193"] = false; + nv6m["zt"] = false; + nv6m["6plane"] = false; + for(std::vector::iterator i(v6ms.begin());i!=v6ms.end();++i) { + if (*i == "rfc4193") + nv6m["rfc4193"] = true; + else if (*i == "zt") + nv6m["zt"] = true; + else if (*i == "6plane") + nv6m["6plane"] = true; + } + } else if (v6m.is_object()) { + if (v6m.count("rfc4193")) nv6m["rfc4193"] = OSUtils::jsonBool(v6m["rfc4193"],false); + if (v6m.count("zt")) nv6m["zt"] = OSUtils::jsonBool(v6m["zt"],false); + if (v6m.count("6plane")) nv6m["6plane"] = OSUtils::jsonBool(v6m["6plane"],false); + } else { + nv6m["rfc4193"] = false; + nv6m["zt"] = false; + nv6m["6plane"] = false; + } + network["v6AssignMode"] = nv6m; + } - json& srv = dns["servers"]; - if (srv.is_array()) { - json ns = json::array(); - for (unsigned int i = 0; i < srv.size(); ++i) { - ns.push_back(srv[i]); - } - nd["servers"] = ns; - } - - network["dns"] = nd; - } - } - - network["id"] = nwids; - network["nwid"] = nwids; - - DB::cleanNetwork(network); - _db.save(network, true); - - return network.dump(); -} - -void EmbeddedNetworkController::configureHTTPControlPlane(httplib::Server& s, httplib::Server& sv6, const std::function setContent) -{ - // Control plane Endpoints - std::string controllerPath = "/controller"; - std::string networkListPath = "/controller/network"; - std::string networkListPath2 = "/unstable/controller/network"; - std::string networkPath = "/controller/network/([0-9a-fA-F]{16})"; - std::string oldAndBustedNetworkCreatePath = "/controller/network/([0-9a-fA-F]{10})______"; - std::string memberListPath = "/controller/network/([0-9a-fA-F]{16})/member"; - std::string memberListPath2 = "/unstable/controller/network/([0-9a-fA-F]{16})/member"; - std::string memberPath = "/controller/network/([0-9a-fA-F]{16})/member/([0-9a-fA-F]{10})"; - - auto controllerGet = [&, setContent](const httplib::Request& req, httplib::Response& res) { - char tmp[4096]; - const bool dbOk = _db.isReady(); - OSUtils::ztsnprintf( - tmp, - sizeof(tmp), - "{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu,\n\t\"databaseReady\": %s\n}\n", - ZT_NETCONF_CONTROLLER_API_VERSION, - (unsigned long long)OSUtils::now(), - dbOk ? "true" : "false"); - - if (! dbOk) { - res.status = 503; - } - - setContent(req, res, tmp); - }; - s.Get(controllerPath, controllerGet); - sv6.Get(controllerPath, controllerGet); - - auto networkListGet = [&, setContent](const httplib::Request& req, httplib::Response& res) { - std::set networkIds; - _db.networks(networkIds); - char tmp[64]; - - auto out = json::array(); - for (std::set::const_iterator i(networkIds.begin()); i != networkIds.end(); ++i) { - OSUtils::ztsnprintf(tmp, sizeof(tmp), "%.16llx", *i); - out.push_back(tmp); - } - - setContent(req, res, out.dump()); - }; - s.Get(networkListPath, networkListGet); - sv6.Get(networkListPath, networkListGet); - - auto networkListGet2 = [&, setContent](const httplib::Request& req, httplib::Response& res) { - std::set networkIds; - _db.networks(networkIds); - - auto meta = json::object(); - auto data = json::array(); - uint64_t networkCount = 0; - - for (std::set::const_iterator nwid(networkIds.begin()); nwid != networkIds.end(); ++nwid) { - json network; - if (! _db.get(*nwid, network)) { - continue; - } - - std::vector memTmp; - if (_db.get(*nwid, network, memTmp)) { - if (! network.is_null()) { - uint64_t authorizedCount = 0; - uint64_t totalCount = memTmp.size(); - networkCount++; - - for (auto m = memTmp.begin(); m != memTmp.end(); ++m) { - bool a = OSUtils::jsonBool((*m)["authorized"], 0); - if (a) { - authorizedCount++; + if (b.count("routes")) { + json &rts = b["routes"]; + if (rts.is_array()) { + json nrts = json::array(); + for(unsigned long i=0;i().c_str()); + InetAddress v; + if (via.is_string()) v.fromString(via.get().c_str()); + if ( ((t.ss_family == AF_INET)||(t.ss_family == AF_INET6)) && (t.netmaskBitsValid()) ) { + json tmp; + char tmp2[64]; + tmp["target"] = t.toString(tmp2); + if (v.ss_family == t.ss_family) + tmp["via"] = v.toIpString(tmp2); + else tmp["via"] = json(); + nrts.push_back(tmp); + if (nrts.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + } + } + } + network["routes"] = nrts; } } - auto nwMeta = json::object(); - nwMeta["totalMemberCount"] = totalCount; - nwMeta["authorizedMemberCount"] = authorizedCount; - network["meta"] = nwMeta; - - data.push_back(network); - } - } - } - meta["networkCount"] = networkCount; - - auto out = json::object(); - out["data"] = data; - out["meta"] = meta; - - setContent(req, res, out.dump()); - }; - s.Get(networkListPath2, networkListGet2); - sv6.Get(networkListPath2, networkListGet2); - - auto networkGet = [&, setContent](const httplib::Request& req, httplib::Response& res) { - auto networkID = req.matches[1]; - uint64_t nwid = Utils::hexStrToU64(networkID.str().c_str()); - json network; - if (! _db.get(nwid, network)) { - res.status = 404; - return; - } - - setContent(req, res, network.dump()); - }; - s.Get(networkPath, networkGet); - sv6.Get(networkPath, networkGet); - - auto createNewNetwork = [&, setContent](const httplib::Request& req, httplib::Response& res) { - // fprintf(stderr, "creating new network (new style)\n"); - uint64_t nwid = 0; - uint64_t nwidPrefix = (Utils::hexStrToU64(_signingIdAddressString.c_str()) << 24) & 0xffffffffff000000ULL; - uint64_t nwidPostfix = 0; - for (unsigned long k = 0; k < 100000; ++k) { // sanity limit on trials - Utils::getSecureRandom(&nwidPostfix, sizeof(nwidPostfix)); - uint64_t tryNwid = nwidPrefix | (nwidPostfix & 0xffffffULL); - if ((tryNwid & 0xffffffULL) == 0ULL) - tryNwid |= 1ULL; - if (! _db.hasNetwork(tryNwid)) { - nwid = tryNwid; - break; - } - } - if (! nwid) { - res.status = 503; - return; - } - - setContent(req, res, networkUpdateFromPostData(nwid, req.body)); - }; - s.Put(networkListPath, createNewNetwork); - s.Post(networkListPath, createNewNetwork); - sv6.Put(networkListPath, createNewNetwork); - sv6.Post(networkListPath, createNewNetwork); - - auto createNewNetworkOldAndBusted = [&, setContent](const httplib::Request& req, httplib::Response& res) { - auto inID = req.matches[1].str(); - - if (inID != _signingIdAddressString) { - res.status = 400; - return; - } - - uint64_t nwid = 0; - uint64_t nwidPrefix = (Utils::hexStrToU64(inID.c_str()) << 24) & 0xffffffffff000000ULL; - uint64_t nwidPostfix = 0; - for (unsigned long k = 0; k < 100000; ++k) { // sanity limit on trials - Utils::getSecureRandom(&nwidPostfix, sizeof(nwidPostfix)); - uint64_t tryNwid = nwidPrefix | (nwidPostfix & 0xffffffULL); - if ((tryNwid & 0xffffffULL) == 0ULL) - tryNwid |= 1ULL; - if (! _db.hasNetwork(tryNwid)) { - nwid = tryNwid; - break; - } - } - if (! nwid) { - res.status = 503; - return; - } - setContent(req, res, networkUpdateFromPostData(nwid, req.body)); - }; - s.Put(oldAndBustedNetworkCreatePath, createNewNetworkOldAndBusted); - s.Post(oldAndBustedNetworkCreatePath, createNewNetworkOldAndBusted); - sv6.Put(oldAndBustedNetworkCreatePath, createNewNetworkOldAndBusted); - sv6.Post(oldAndBustedNetworkCreatePath, createNewNetworkOldAndBusted); - - auto networkPost = [&, setContent](const httplib::Request& req, httplib::Response& res) { - auto networkID = req.matches[1].str(); - uint64_t nwid = Utils::hexStrToU64(networkID.c_str()); - - res.status = 200; - setContent(req, res, networkUpdateFromPostData(nwid, req.body)); - }; - s.Put(networkPath, networkPost); - s.Post(networkPath, networkPost); - sv6.Put(networkPath, networkPost); - sv6.Post(networkPath, networkPost); - - auto networkDelete = [&, setContent](const httplib::Request& req, httplib::Response& res) { - auto networkID = req.matches[1].str(); - uint64_t nwid = Utils::hexStrToU64(networkID.c_str()); - - json network; - if (! _db.get(nwid, network)) { - res.status = 404; - return; - } - - _db.eraseNetwork(nwid); - setContent(req, res, network.dump()); - }; - s.Delete(networkPath, networkDelete); - sv6.Delete(networkPath, networkDelete); - - auto memberListGet = [&, setContent](const httplib::Request& req, httplib::Response& res) { - auto networkID = req.matches[1]; - uint64_t nwid = Utils::hexStrToU64(networkID.str().c_str()); - json network; - if (! _db.get(nwid, network)) { - res.status = 404; - return; - } - - json out = json::object(); - std::vector memTmp; - if (_db.get(nwid, network, memTmp)) { - for (auto m = memTmp.begin(); m != memTmp.end(); ++m) { - int revision = OSUtils::jsonInt((*m)["revision"], 0); - std::string id = OSUtils::jsonString((*m)["id"], ""); - if (id.length() == 10) { - out[id] = revision; - } - } - } - - setContent(req, res, out.dump()); - }; - s.Get(memberListPath, memberListGet); - sv6.Get(memberListPath, memberListGet); - - auto memberListGet2 = [&, setContent](const httplib::Request& req, httplib::Response& res) { - auto networkID = req.matches[1]; - uint64_t nwid = Utils::hexStrToU64(networkID.str().c_str()); - json network; - if (! _db.get(nwid, network)) { - res.status = 404; - return; - } - - auto out = nlohmann::json::object(); - auto meta = nlohmann::json::object(); - std::vector memTmp; - if (_db.get(nwid, network, memTmp)) { - uint64_t authorizedCount = 0; - uint64_t totalCount = memTmp.size(); - for (auto m = memTmp.begin(); m != memTmp.end(); ++m) { - bool a = OSUtils::jsonBool((*m)["authorized"], 0); - if (a) { - authorizedCount++; - } - } - - meta["totalCount"] = totalCount; - meta["authorizedCount"] = authorizedCount; - - out["data"] = memTmp; - out["meta"] = meta; - - setContent(req, res, out.dump()); - } - else { - res.status = 404; - return; - } - }; - s.Get(memberListPath2, memberListGet2); - sv6.Get(memberListPath2, memberListGet2); - - auto memberGet = [&, setContent](const httplib::Request& req, httplib::Response& res) { - auto networkID = req.matches[1]; - auto memberID = req.matches[2]; - uint64_t nwid = Utils::hexStrToU64(networkID.str().c_str()); - uint64_t memid = Utils::hexStrToU64(memberID.str().c_str()); - json network; - json member; - if (! _db.get(nwid, network, memid, member)) { - res.status = 404; - return; - } - - setContent(req, res, member.dump()); - }; - s.Get(memberPath, memberGet); - sv6.Get(memberPath, memberGet); - - auto memberPost = [&, setContent](const httplib::Request& req, httplib::Response& res) { - auto networkID = req.matches[1].str(); - auto memberID = req.matches[2].str(); - uint64_t nwid = Utils::hexStrToU64(networkID.c_str()); - uint64_t memid = Utils::hexStrToU64(memberID.c_str()); - - if (! _db.hasNetwork(nwid)) { - res.status = 404; - return; - } - - json network; - json member; - _db.get(nwid, network, memid, member); - DB::initMember(member); - - json b = OSUtils::jsonParse(req.body); - - if (b.count("activeBridge")) - member["activeBridge"] = OSUtils::jsonBool(b["activeBridge"], false); - if (b.count("noAutoAssignIps")) - member["noAutoAssignIps"] = OSUtils::jsonBool(b["noAutoAssignIps"], false); - if (b.count("authenticationExpiryTime")) - member["authenticationExpiryTime"] = (uint64_t)OSUtils::jsonInt(b["authenticationExpiryTime"], 0ULL); - if (b.count("authenticationURL")) - member["authenticationURL"] = OSUtils::jsonString(b["authenticationURL"], ""); - if (b.count("name")) - member["name"] = OSUtils::jsonString(b["name"], ""); - - if (b.count("remoteTraceTarget")) { - const std::string rtt(OSUtils::jsonString(b["remoteTraceTarget"], "")); - if (rtt.length() == 10) { - member["remoteTraceTarget"] = rtt; - } - else { - member["remoteTraceTarget"] = json(); - } - } - if (b.count("remoteTraceLevel")) - member["remoteTraceLevel"] = OSUtils::jsonInt(b["remoteTraceLevel"], 0ULL); - - if (b.count("authorized")) { - const bool newAuth = OSUtils::jsonBool(b["authorized"], false); - if (newAuth != OSUtils::jsonBool(member["authorized"], false)) { - member["authorized"] = newAuth; - member[((newAuth) ? "lastAuthorizedTime" : "lastDeauthorizedTime")] = OSUtils::now(); - if (newAuth) { - member["lastAuthorizedCredentialType"] = "api"; - member["lastAuthorizedCredential"] = json(); - } - } - } - - if (b.count("ipAssignments")) { - json& ipa = b["ipAssignments"]; - if (ipa.is_array()) { - json mipa(json::array()); - for (unsigned long i = 0; i < ipa.size(); ++i) { - std::string ips = ipa[i]; - InetAddress ip(ips.c_str()); - if ((ip.ss_family == AF_INET) || (ip.ss_family == AF_INET6)) { - char tmpip[64]; - mipa.push_back(ip.toIpString(tmpip)); - if (mipa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) - break; + if (b.count("ipAssignmentPools")) { + json &ipp = b["ipAssignmentPools"]; + if (ipp.is_array()) { + json nipp = json::array(); + for(unsigned long i=0;i= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + } + } + network["ipAssignmentPools"] = nipp; + } } + + if (b.count("rules")) { + json &rules = b["rules"]; + if (rules.is_array()) { + json nrules = json::array(); + for(unsigned long i=0;i= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + } + } + network["rules"] = nrules; + } + } + + if (b.count("authTokens")) { + json &authTokens = b["authTokens"]; + if (authTokens.is_object()) { + json nat; + for(json::iterator t(authTokens.begin());t!=authTokens.end();++t) { + if ((t.value().is_number())&&(t.value() >= 0)) + nat[t.key()] = t.value(); + } + network["authTokens"] = nat; + } else { + network["authTokens"] = {{}}; + } + } + + if (b.count("capabilities")) { + json &capabilities = b["capabilities"]; + if (capabilities.is_array()) { + std::map< uint64_t,json > ncaps; + for(unsigned long i=0;i= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + } + } + } + ncap["rules"] = nrules; + + ncaps[capId] = ncap; + } + } + + json ncapsa = json::array(); + for(std::map< uint64_t,json >::iterator c(ncaps.begin());c!=ncaps.end();++c) { + ncapsa.push_back(c->second); + if (ncapsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + network["capabilities"] = ncapsa; + } + } + + if (b.count("tags")) { + json &tags = b["tags"]; + if (tags.is_array()) { + std::map< uint64_t,json > ntags; + for(unsigned long i=0;i::iterator t(ntags.begin());t!=ntags.end();++t) { + ntagsa.push_back(t->second); + if (ntagsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) + break; + } + network["tags"] = ntagsa; + } + } + + } catch ( ... ) { + responseBody = "{ \"message\": \"exception occurred while parsing body variables\" }"; + responseContentType = "application/json"; + return 400; } - member["ipAssignments"] = mipa; - } - } - if (b.count("tags")) { - json& tags = b["tags"]; - if (tags.is_array()) { - std::map mtags; - for (unsigned long i = 0; i < tags.size(); ++i) { - json& tag = tags[i]; - if ((tag.is_array()) && (tag.size() == 2)) - mtags[OSUtils::jsonInt(tag[0], 0ULL) & 0xffffffffULL] = OSUtils::jsonInt(tag[1], 0ULL) & 0xffffffffULL; - } - json mtagsa = json::array(); - for (std::map::iterator t(mtags.begin()); t != mtags.end(); ++t) { - json ta = json::array(); - ta.push_back(t->first); - ta.push_back(t->second); - mtagsa.push_back(ta); - if (mtagsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) - break; - } - member["tags"] = mtagsa; - } - } + network["id"] = nwids; + network["nwid"] = nwids; // legacy - if (b.count("capabilities")) { - json& capabilities = b["capabilities"]; - if (capabilities.is_array()) { - json mcaps = json::array(); - for (unsigned long i = 0; i < capabilities.size(); ++i) { - mcaps.push_back(OSUtils::jsonInt(capabilities[i], 0ULL)); - if (mcaps.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE) - break; - } - std::sort(mcaps.begin(), mcaps.end()); - mcaps.erase(std::unique(mcaps.begin(), mcaps.end()), mcaps.end()); - member["capabilities"] = mcaps; - } - } + DB::cleanNetwork(network); + _db->save(&origNetwork,network); - member["id"] = memberID; - member["address"] = memberID; - member["nwid"] = networkID; + responseBody = OSUtils::jsonDump(network); + responseContentType = "application/json"; + return 200; + } // else 404 - DB::cleanMember(member); - _db.save(member, true); + } // else 404 - setContent(req, res, member.dump()); - }; - s.Put(memberPath, memberPost); - s.Post(memberPath, memberPost); - sv6.Put(memberPath, memberPost); - sv6.Post(memberPath, memberPost); + } - auto memberDelete = [&, setContent](const httplib::Request& req, httplib::Response& res) { - auto networkID = req.matches[1].str(); - auto memberID = req.matches[2].str(); - - uint64_t nwid = Utils::hexStrToU64(networkID.c_str()); - uint64_t address = Utils::hexStrToU64(memberID.c_str()); - json network, member; - - if (! _db.get(nwid, network, address, member)) { - res.status = 404; - return; - } - - if (! member.size()) { - res.status = 404; - return; - } - - _db.eraseMember(nwid, address); - - setContent(req, res, member.dump()); - }; - s.Delete(memberPath, memberDelete); - sv6.Delete(memberPath, memberDelete); + return 404; } -void EmbeddedNetworkController::handleRemoteTrace(const ZT_RemoteTrace& rt) +unsigned int EmbeddedNetworkController::handleControlPlaneHttpDELETE( + const std::vector &path, + const std::map &urlArgs, + const std::map &headers, + const std::string &body, + std::string &responseBody, + std::string &responseContentType) +{ + if (!_db) + return 500; + if (path.empty()) + return 404; + + if (path[0] == "network") { + if ((path.size() >= 2)&&(path[1].length() == 16)) { + const uint64_t nwid = Utils::hexStrToU64(path[1].c_str()); + if (path.size() >= 3) { + if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) { + const uint64_t address = Utils::hexStrToU64(path[3].c_str()); + + json network,member; + _db->get(nwid,network,address,member); + _db->eraseMember(nwid, address); + + { + std::lock_guard l(_memberStatus_l); + _memberStatus.erase(_MemberStatusKey(nwid,address)); + } + + if (!member.size()) + return 404; + responseBody = OSUtils::jsonDump(member); + responseContentType = "application/json"; + return 200; + } + } else { + json network; + _db->get(nwid,network); + _db->eraseNetwork(nwid); + + { + std::lock_guard l(_memberStatus_l); + for(auto i=_memberStatus.begin();i!=_memberStatus.end();) { + if (i->first.networkId == nwid) + _memberStatus.erase(i++); + else ++i; + } + } + + if (!network.size()) + return 404; + responseBody = OSUtils::jsonDump(network); + responseContentType = "application/json"; + return 200; + } + } // else 404 + + } // else 404 + + return 404; +} + +void EmbeddedNetworkController::handleRemoteTrace(const ZT_RemoteTrace &rt) { static volatile unsigned long idCounter = 0; - char id[128], tmp[128]; - std::string k, v; + char id[128],tmp[128]; + std::string k,v; + + if (!_db) + return; try { // Convert Dictionary into JSON object json d; - char* saveptr = (char*)0; - for (char* l = Utils::stok(rt.data, "\n", &saveptr); (l); l = Utils::stok((char*)0, "\n", &saveptr)) { - char* eq = strchr(l, '='); + char *saveptr = (char *)0; + for(char *l=Utils::stok(rt.data,"\n",&saveptr);(l);l=Utils::stok((char *)0,"\n",&saveptr)) { + char *eq = strchr(l,'='); if (eq > l) { - k.assign(l, (unsigned long)(eq - l)); + k.assign(l,(unsigned long)(eq - l)); v.clear(); ++eq; while (*eq) { if (*eq == '\\') { ++eq; if (*eq) { - switch (*eq) { - case 'r': - v.push_back('\r'); - break; - case 'n': - v.push_back('\n'); - break; - case '0': - v.push_back((char)0); - break; - case 'e': - v.push_back('='); - break; - default: - v.push_back(*eq); - break; + switch(*eq) { + case 'r': v.push_back('\r'); break; + case 'n': v.push_back('\n'); break; + case '0': v.push_back((char)0); break; + case 'e': v.push_back('='); break; + default: v.push_back(*eq); break; } ++eq; } - } - else { + } else { v.push_back(*(eq++)); } } - if ((k.length() > 0) && (v.length() > 0)) + if ((k.length() > 0)&&(v.length() > 0)) d[k] = v; } } const int64_t now = OSUtils::now(); - OSUtils::ztsnprintf(id, sizeof(id), "%.10llx-%.16llx-%.10llx-%.4x", _signingId.address().toInt(), now, rt.origin, (unsigned int)(idCounter++ & 0xffff)); + OSUtils::ztsnprintf(id,sizeof(id),"%.10llx-%.16llx-%.10llx-%.4x",_signingId.address().toInt(),now,rt.origin,(unsigned int)(idCounter++ & 0xffff)); d["id"] = id; d["objtype"] = "trace"; d["ts"] = now; - d["nodeId"] = Utils::hex10(rt.origin, tmp); - _db.save(d, true); - } - catch (...) { + d["nodeId"] = Utils::hex10(rt.origin,tmp); + _db->save((nlohmann::json *)0,d); + } catch ( ... ) { // drop invalid trace messages if an error occurs } } -void EmbeddedNetworkController::onNetworkUpdate(const void* db, uint64_t networkId, const nlohmann::json& network) +void EmbeddedNetworkController::onNetworkUpdate(const uint64_t networkId,const nlohmann::json &network) { // Send an update to all members of the network that are online const int64_t now = OSUtils::now(); std::lock_guard l(_memberStatus_l); - for (auto i = _memberStatus.begin(); i != _memberStatus.end(); ++i) { - if ((i->first.networkId == networkId) && (i->second.online(now)) && (i->second.lastRequestMetaData)) - request(networkId, InetAddress(), 0, i->second.identity, i->second.lastRequestMetaData); + for(auto i=_memberStatus.begin();i!=_memberStatus.end();++i) { + if ((i->first.networkId == networkId)&&(i->second.online(now))&&(i->second.lastRequestMetaData)) + request(networkId,InetAddress(),0,i->second.identity,i->second.lastRequestMetaData); } } -void EmbeddedNetworkController::onNetworkMemberUpdate(const void* db, uint64_t networkId, uint64_t memberId, const nlohmann::json& member) +void EmbeddedNetworkController::onNetworkMemberUpdate(const uint64_t networkId,const uint64_t memberId,const nlohmann::json &member) { // Push update to member if online try { std::lock_guard l(_memberStatus_l); - _MemberStatus& ms = _memberStatus[_MemberStatusKey(networkId, memberId)]; - if ((ms.online(OSUtils::now())) && (ms.lastRequestMetaData)) - request(networkId, InetAddress(), 0, ms.identity, ms.lastRequestMetaData); - } - catch (...) { - } + _MemberStatus &ms = _memberStatus[_MemberStatusKey(networkId,memberId)]; + if ((ms.online(OSUtils::now()))&&(ms.lastRequestMetaData)) + request(networkId,InetAddress(),0,ms.identity,ms.lastRequestMetaData); + } catch ( ... ) {} } -void EmbeddedNetworkController::onNetworkMemberDeauthorize(const void* db, uint64_t networkId, uint64_t memberId) +void EmbeddedNetworkController::onNetworkMemberDeauthorize(const uint64_t networkId,const uint64_t memberId) { const int64_t now = OSUtils::now(); - Revocation rev((uint32_t)_node->prng(), networkId, 0, now, ZT_REVOCATION_FLAG_FAST_PROPAGATE, Address(memberId), Revocation::CREDENTIAL_TYPE_COM); + Revocation rev((uint32_t)_node->prng(),networkId,0,now,ZT_REVOCATION_FLAG_FAST_PROPAGATE,Address(memberId),Revocation::CREDENTIAL_TYPE_COM); rev.sign(_signingId); { std::lock_guard l(_memberStatus_l); - for (auto i = _memberStatus.begin(); i != _memberStatus.end(); ++i) { - if ((i->first.networkId == networkId) && (i->second.online(now))) - _node->ncSendRevocation(Address(i->first.nodeId), rev); + for(auto i=_memberStatus.begin();i!=_memberStatus.end();++i) { + if ((i->first.networkId == networkId)&&(i->second.online(now))) + _node->ncSendRevocation(Address(i->first.nodeId),rev); } } } -void EmbeddedNetworkController::_request(uint64_t nwid, const InetAddress& fromAddr, uint64_t requestPacketId, const Identity& identity, const Dictionary& metaData) +void EmbeddedNetworkController::_request( + uint64_t nwid, + const InetAddress &fromAddr, + uint64_t requestPacketId, + const Identity &identity, + const Dictionary &metaData) { - Metrics::network_config_request++; - auto tid = std::this_thread::get_id(); - std::stringstream ss; - ss << tid; - std::string threadID = ss.str(); -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - auto b1 = _member_status_lookup.Add({ { "thread", threadID } }); - auto c1 = _member_status_lookup_count.Add({ { "thread", threadID } }); - c1++; - b1.start(); -#endif - char nwids[24]; DB::NetworkSummaryInfo ns; - json network, member; + json network,member,origMember; - if (((! _signingId) || (! _signingId.hasPrivate())) || (_signingId.address().toInt() != (nwid >> 24)) || (! _sender)) { + if (!_db) + return; + + if (((!_signingId)||(!_signingId.hasPrivate()))||(_signingId.address().toInt() != (nwid >> 24))||(!_sender)) return; - } const int64_t now = OSUtils::now(); -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b1.stop(); - auto b2 = _node_is_online.Add({ { "thread", threadID } }); - auto c2 = _node_is_online_count.Add({ { "thread", threadID } }); - c2++; - b2.start(); -#endif - char osArch[256]; - metaData.get(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_OS_ARCH, osArch, sizeof(osArch)); - // fprintf(stderr, "Network Config Request: nwid=%.16llx, nodeid=%.10llx, osArch=%s\n", - // nwid, identity.address().toInt(), osArch); - _db.nodeIsOnline(nwid, identity.address().toInt(), fromAddr, osArch); -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b2.stop(); + if (requestPacketId) { + std::lock_guard l(_memberStatus_l); + _MemberStatus &ms = _memberStatus[_MemberStatusKey(nwid,identity.address().toInt())]; + if ((now - ms.lastRequestTime) <= ZT_NETCONF_MIN_REQUEST_PERIOD) + return; + ms.lastRequestTime = now; + } - auto b3 = _get_and_init_member.Add({ { "thread", threadID } }); - auto c3 = _get_and_init_member_count.Add({ { "thread", threadID } }); - c3++; - b3.start(); -#endif - Utils::hex(nwid, nwids); - _db.get(nwid, network, identity.address().toInt(), member, ns); - if ((! network.is_object()) || (network.empty())) { - _sender->ncSendError(nwid, requestPacketId, identity.address(), NetworkController::NC_ERROR_OBJECT_NOT_FOUND, nullptr, 0); -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b3.stop(); -#endif + _db->nodeIsOnline(nwid,identity.address().toInt(),fromAddr); + + Utils::hex(nwid,nwids); + _db->get(nwid,network,identity.address().toInt(),member,ns); + if ((!network.is_object())||(network.size() == 0)) { + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_OBJECT_NOT_FOUND); return; } - const bool newMember = ((! member.is_object()) || (member.empty())); + origMember = member; + const bool newMember = ((!member.is_object())||(member.size() == 0)); DB::initMember(member); - _MemberStatusKey msk(nwid, identity.address().toInt()); -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b3.stop(); -#endif { -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - auto b4 = _have_identity.Add({ { "thread", threadID } }); - auto c4 = _have_identity_count.Add({ { "thread", threadID } }); - c4++; - b4.start(); -#endif - const std::string haveIdStr(OSUtils::jsonString(member["identity"], "")); + const std::string haveIdStr(OSUtils::jsonString(member["identity"],"")); if (haveIdStr.length() > 0) { // If we already know this member's identity perform a full compare. This prevents // a "collision" from being able to auth onto our network in place of an already // known member. try { if (Identity(haveIdStr.c_str()) != identity) { - _sender->ncSendError(nwid, requestPacketId, identity.address(), NetworkController::NC_ERROR_ACCESS_DENIED, nullptr, 0); -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b4.stop(); -#endif + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); return; } - } - catch (...) { - _sender->ncSendError(nwid, requestPacketId, identity.address(), NetworkController::NC_ERROR_ACCESS_DENIED, nullptr, 0); -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b4.stop(); -#endif + } catch ( ... ) { + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); return; } - } - else { + } else { // If we do not yet know this member's identity, learn it. char idtmp[1024]; - member["identity"] = identity.toString(false, idtmp); + member["identity"] = identity.toString(false,idtmp); } -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b4.stop(); -#endif } // These are always the same, but make sure they are set @@ -1581,33 +1297,25 @@ void EmbeddedNetworkController::_request(uint64_t nwid, const InetAddress& fromA } // Determine whether and how member is authorized -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - auto b5 = _determine_auth.Add({ { "thread", threadID } }); - auto c5 = _determine_auth_count.Add({ { "thread", threadID } }); - c5++; - b5.start(); -#endif bool authorized = false; bool autoAuthorized = false; - json autoAuthCredentialType, autoAuthCredential; - if (OSUtils::jsonBool(member["authorized"], false)) { + json autoAuthCredentialType,autoAuthCredential; + if (OSUtils::jsonBool(member["authorized"],false)) { authorized = true; - } - else if (! OSUtils::jsonBool(network["private"], true)) { + } else if (!OSUtils::jsonBool(network["private"],true)) { authorized = true; autoAuthorized = true; autoAuthCredentialType = "public"; - } - else { + } else { char presentedAuth[512]; - if (metaData.get(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH, presentedAuth, sizeof(presentedAuth)) > 0) { - presentedAuth[511] = (char)0; // sanity check - if ((strlen(presentedAuth) > 6) && (! strncmp(presentedAuth, "token:", 6))) { - const char* const presentedToken = presentedAuth + 6; + if (metaData.get(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH,presentedAuth,sizeof(presentedAuth)) > 0) { + presentedAuth[511] = (char)0; // sanity check + if ((strlen(presentedAuth) > 6)&&(!strncmp(presentedAuth,"token:",6))) { + const char *const presentedToken = presentedAuth + 6; json authTokens(network["authTokens"]); - json& tokenExpires = authTokens[presentedToken]; + json &tokenExpires = authTokens[presentedToken]; if (tokenExpires.is_number()) { - if ((tokenExpires == 0) || (tokenExpires > now)) { + if ((tokenExpires == 0)||(tokenExpires > now)) { authorized = true; autoAuthorized = true; autoAuthCredentialType = "token"; @@ -1619,74 +1327,20 @@ void EmbeddedNetworkController::_request(uint64_t nwid, const InetAddress& fromA } // If we auto-authorized, update member record - if ((autoAuthorized) && (authorized)) { + if ((autoAuthorized)&&(authorized)) { member["authorized"] = true; member["lastAuthorizedTime"] = now; member["lastAuthorizedCredentialType"] = autoAuthCredentialType; member["lastAuthorizedCredential"] = autoAuthCredential; } -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b5.stop(); -#endif - // Should we check SSO Stuff? - // If network is configured with SSO, and the member is not marked exempt: yes - // Otherwise no, we use standard auth logic. -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - auto b6 = _sso_check.Add({ { "thread", threadID } }); - auto c6 = _sso_check_count.Add({ { "thread", threadID } }); - c6++; - b6.start(); -#endif - AuthInfo info; - int64_t authenticationExpiryTime = -1; - bool networkSSOEnabled = OSUtils::jsonBool(network["ssoEnabled"], false); - bool memberSSOExempt = OSUtils::jsonBool(member["ssoExempt"], false); - if (networkSSOEnabled && ! memberSSOExempt) { - authenticationExpiryTime = (int64_t)OSUtils::jsonInt(member["authenticationExpiryTime"], 0); - info = _db.getSSOAuthInfo(member, _ssoRedirectURL); - assert(info.enabled == networkSSOEnabled); - if (authenticationExpiryTime <= now) { - if (info.version == 0) { - Dictionary<4096> authInfo; - authInfo.add(ZT_AUTHINFO_DICT_KEY_VERSION, (uint64_t)0ULL); - authInfo.add(ZT_AUTHINFO_DICT_KEY_AUTHENTICATION_URL, info.authenticationURL.c_str()); - _sender->ncSendError(nwid, requestPacketId, identity.address(), NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes()); - } - else if (info.version == 1) { - Dictionary<8192> authInfo; - authInfo.add(ZT_AUTHINFO_DICT_KEY_VERSION, info.version); - authInfo.add(ZT_AUTHINFO_DICT_KEY_ISSUER_URL, info.issuerURL.c_str()); - authInfo.add(ZT_AUTHINFO_DICT_KEY_CENTRAL_ENDPOINT_URL, info.centralAuthURL.c_str()); - authInfo.add(ZT_AUTHINFO_DICT_KEY_NONCE, info.ssoNonce.c_str()); - authInfo.add(ZT_AUTHINFO_DICT_KEY_STATE, info.ssoState.c_str()); - authInfo.add(ZT_AUTHINFO_DICT_KEY_CLIENT_ID, info.ssoClientID.c_str()); - authInfo.add(ZT_AUTHINFO_DICT_KEY_SSO_PROVIDER, info.ssoProvider.c_str()); - _sender->ncSendError(nwid, requestPacketId, identity.address(), NetworkController::NC_ERROR_AUTHENTICATION_REQUIRED, authInfo.data(), authInfo.sizeBytes()); - } - DB::cleanMember(member); - _db.save(member, true); -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b6.stop(); -#endif - return; - } - } -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b6.stop(); - - auto b7 = _auth_check.Add({ { "thread", threadID } }); - auto c7 = _auth_check_count.Add({ { "thread", threadID } }); - c7++; - b7.start(); -#endif if (authorized) { // Update version info and meta-data if authorized and if this is a genuine request if (requestPacketId) { - const uint64_t vMajor = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION, 0); - const uint64_t vMinor = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION, 0); - const uint64_t vRev = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION, 0); - const uint64_t vProto = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION, 0); + const uint64_t vMajor = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0); + const uint64_t vMinor = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0); + const uint64_t vRev = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0); + const uint64_t vProto = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION,0); member["vMajor"] = vMajor; member["vMinor"] = vMinor; @@ -1695,8 +1349,8 @@ void EmbeddedNetworkController::_request(uint64_t nwid, const InetAddress& fromA { std::lock_guard l(_memberStatus_l); - _MemberStatus& ms = _memberStatus[msk]; - ms.authenticationExpiryTime = authenticationExpiryTime; + _MemberStatus &ms = _memberStatus[_MemberStatusKey(nwid,identity.address().toInt())]; + ms.vMajor = (int)vMajor; ms.vMinor = (int)vMinor; ms.vRev = (int)vRev; @@ -1704,185 +1358,130 @@ void EmbeddedNetworkController::_request(uint64_t nwid, const InetAddress& fromA ms.lastRequestMetaData = metaData; ms.identity = identity; } - - if (authenticationExpiryTime > 0) { - std::lock_guard l(_expiringSoon_l); - _expiringSoon.insert(std::pair(authenticationExpiryTime, msk)); - } } - } - else { + } else { // If they are not authorized, STOP! DB::cleanMember(member); - _db.save(member, true); - _sender->ncSendError(nwid, requestPacketId, identity.address(), NetworkController::NC_ERROR_ACCESS_DENIED, nullptr, 0); -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b7.stop(); -#endif + _db->save(&origMember,member); + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); return; } -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b7.stop(); -#endif // ------------------------------------------------------------------------- - // If we made it this far, they are authorized (and authenticated). + // If we made it this far, they are authorized. // ------------------------------------------------------------------------- - // Default timeout: 15 minutes. Maximum: two hours. Can be specified by an optional field in the network config - // if something longer than 15 minutes is desired. Minimum is 5 minutes since shorter than that would be flaky. -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - auto b8 = _json_schlep.Add({ { "thread", threadID } }); - auto c8 = _json_schlep_count.Add({ { "thread", threadID } }); - c8++; - b8.start(); -#endif - int64_t credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_DFL_MAX_DELTA; - if (network.contains("certificateTimeoutWindowSize")) { - credentialtmd = (int64_t)network["certificateTimeoutWindowSize"]; + int64_t credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA; + if (now > ns.mostRecentDeauthTime) { + // If we recently de-authorized a member, shrink credential TTL/max delta to + // be below the threshold required to exclude it. Cap this to a min/max to + // prevent jitter or absurdly large values. + const uint64_t deauthWindow = now - ns.mostRecentDeauthTime; + if (deauthWindow < ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA) { + credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA; + } else if (deauthWindow < (ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA + 5000ULL)) { + credentialtmd = deauthWindow - 5000ULL; + } } - credentialtmd = std::max(std::min(credentialtmd, ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA), ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA); std::unique_ptr nc(new NetworkConfig()); nc->networkId = nwid; - nc->type = OSUtils::jsonBool(network["private"], true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC; + nc->type = OSUtils::jsonBool(network["private"],true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC; nc->timestamp = now; nc->credentialTimeMaxDelta = credentialtmd; - nc->revision = OSUtils::jsonInt(network["revision"], 0ULL); + nc->revision = OSUtils::jsonInt(network["revision"],0ULL); nc->issuedTo = identity.address(); - if (OSUtils::jsonBool(network["enableBroadcast"], true)) - nc->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST; - Utils::scopy(nc->name, sizeof(nc->name), OSUtils::jsonString(network["name"], "").c_str()); - nc->mtu = std::max(std::min((unsigned int)OSUtils::jsonInt(network["mtu"], ZT_DEFAULT_MTU), (unsigned int)ZT_MAX_MTU), (unsigned int)ZT_MIN_MTU); - nc->multicastLimit = (unsigned int)OSUtils::jsonInt(network["multicastLimit"], 32ULL); + if (OSUtils::jsonBool(network["enableBroadcast"],true)) nc->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST; + Utils::scopy(nc->name,sizeof(nc->name),OSUtils::jsonString(network["name"],"").c_str()); + nc->mtu = std::max(std::min((unsigned int)OSUtils::jsonInt(network["mtu"],ZT_DEFAULT_MTU),(unsigned int)ZT_MAX_MTU),(unsigned int)ZT_MIN_MTU); + nc->multicastLimit = (unsigned int)OSUtils::jsonInt(network["multicastLimit"],32ULL); - nc->ssoEnabled = networkSSOEnabled; // OSUtils::jsonBool(network["ssoEnabled"], false); - nc->ssoVersion = info.version; - - if (info.version == 0) { - nc->authenticationExpiryTime = OSUtils::jsonInt(member["authenticationExpiryTime"], 0LL); - if (! info.authenticationURL.empty()) { - Utils::scopy(nc->authenticationURL, sizeof(nc->authenticationURL), info.authenticationURL.c_str()); - } - } - else if (info.version == 1) { - nc->authenticationExpiryTime = OSUtils::jsonInt(member["authenticationExpiryTime"], 0LL); - if (! info.authenticationURL.empty()) { - Utils::scopy(nc->authenticationURL, sizeof(nc->authenticationURL), info.authenticationURL.c_str()); - } - if (! info.centralAuthURL.empty()) { - Utils::scopy(nc->centralAuthURL, sizeof(nc->centralAuthURL), info.centralAuthURL.c_str()); - } - if (! info.issuerURL.empty()) { -#ifdef ZT_DEBUG - fprintf(stderr, "copying issuerURL to nc: %s\n", info.issuerURL.c_str()); -#endif - Utils::scopy(nc->issuerURL, sizeof(nc->issuerURL), info.issuerURL.c_str()); - } - if (! info.ssoNonce.empty()) { - Utils::scopy(nc->ssoNonce, sizeof(nc->ssoNonce), info.ssoNonce.c_str()); - } - if (! info.ssoState.empty()) { - Utils::scopy(nc->ssoState, sizeof(nc->ssoState), info.ssoState.c_str()); - } - if (! info.ssoClientID.empty()) { - Utils::scopy(nc->ssoClientID, sizeof(nc->ssoClientID), info.ssoClientID.c_str()); - } - } - - std::string rtt(OSUtils::jsonString(member["remoteTraceTarget"], "")); + std::string rtt(OSUtils::jsonString(member["remoteTraceTarget"],"")); if (rtt.length() == 10) { nc->remoteTraceTarget = Address(Utils::hexStrToU64(rtt.c_str())); - nc->remoteTraceLevel = (Trace::Level)OSUtils::jsonInt(member["remoteTraceLevel"], 0ULL); - } - else { - rtt = OSUtils::jsonString(network["remoteTraceTarget"], ""); + nc->remoteTraceLevel = (Trace::Level)OSUtils::jsonInt(member["remoteTraceLevel"],0ULL); + } else { + rtt = OSUtils::jsonString(network["remoteTraceTarget"],""); if (rtt.length() == 10) { nc->remoteTraceTarget = Address(Utils::hexStrToU64(rtt.c_str())); - } - else { + } else { nc->remoteTraceTarget.zero(); } - nc->remoteTraceLevel = (Trace::Level)OSUtils::jsonInt(network["remoteTraceLevel"], 0ULL); + nc->remoteTraceLevel = (Trace::Level)OSUtils::jsonInt(network["remoteTraceLevel"],0ULL); } - for (std::vector
::const_iterator ab(ns.activeBridges.begin()); ab != ns.activeBridges.end(); ++ab) { - nc->addSpecialist(*ab, ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE); - } + for(std::vector
::const_iterator ab(ns.activeBridges.begin());ab!=ns.activeBridges.end();++ab) + nc->addSpecialist(*ab,ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE); - json& v4AssignMode = network["v4AssignMode"]; - json& v6AssignMode = network["v6AssignMode"]; - json& ipAssignmentPools = network["ipAssignmentPools"]; - json& routes = network["routes"]; - json& rules = network["rules"]; - json& capabilities = network["capabilities"]; - json& tags = network["tags"]; - json& memberCapabilities = member["capabilities"]; - json& memberTags = member["tags"]; - json& dns = network["dns"]; + json &v4AssignMode = network["v4AssignMode"]; + json &v6AssignMode = network["v6AssignMode"]; + json &ipAssignmentPools = network["ipAssignmentPools"]; + json &routes = network["routes"]; + json &rules = network["rules"]; + json &capabilities = network["capabilities"]; + json &tags = network["tags"]; + json &memberCapabilities = member["capabilities"]; + json &memberTags = member["tags"]; - // fprintf(stderr, "IP Assignment Pools for Network %s: %s\n", nwids, OSUtils::jsonDump(ipAssignmentPools, 2).c_str()); - - if (metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV, 0) <= 0) { + if (metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV,0) <= 0) { // Old versions with no rules engine support get an allow everything rule. // Since rules are enforced bidirectionally, newer versions *will* still // enforce rules on the inbound side. nc->ruleCount = 1; nc->rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT; - } - else { + } else { if (rules.is_array()) { - for (unsigned long i = 0; i < rules.size(); ++i) { + for(unsigned long i=0;iruleCount >= ZT_MAX_NETWORK_RULES) break; - if (_parseRule(rules[i], nc->rules[nc->ruleCount])) + if (_parseRule(rules[i],nc->rules[nc->ruleCount])) ++nc->ruleCount; } } - std::map capsById; - if (! memberCapabilities.is_array()) + std::map< uint64_t,json * > capsById; + if (!memberCapabilities.is_array()) memberCapabilities = json::array(); if (capabilities.is_array()) { - for (unsigned long i = 0; i < capabilities.size(); ++i) { - json& cap = capabilities[i]; + for(unsigned long i=0;i::const_iterator ctmp = capsById.find(capId); + for(unsigned long i=0;i::const_iterator ctmp = capsById.find(capId); if (ctmp != capsById.end()) { - json* cap = ctmp->second; - if ((cap) && (cap->is_object()) && (! cap->empty())) { + json *cap = ctmp->second; + if ((cap)&&(cap->is_object())&&(cap->size() > 0)) { ZT_VirtualNetworkRule capr[ZT_MAX_CAPABILITY_RULES]; unsigned int caprc = 0; - json& caprj = (*cap)["rules"]; - if ((caprj.is_array()) && (! caprj.empty())) { - for (unsigned long j = 0; j < caprj.size(); ++j) { + json &caprj = (*cap)["rules"]; + if ((caprj.is_array())&&(caprj.size() > 0)) { + for(unsigned long j=0;j= ZT_MAX_CAPABILITY_RULES) break; - if (_parseRule(caprj[j], capr[caprc])) + if (_parseRule(caprj[j],capr[caprc])) ++caprc; } } - nc->capabilities[nc->capabilityCount] = Capability((uint32_t)capId, nwid, now, 1, capr, caprc); - if (nc->capabilities[nc->capabilityCount].sign(_signingId, identity.address())) + nc->capabilities[nc->capabilityCount] = Capability((uint32_t)capId,nwid,now,1,capr,caprc); + if (nc->capabilities[nc->capabilityCount].sign(_signingId,identity.address())) ++nc->capabilityCount; if (nc->capabilityCount >= ZT_MAX_NETWORK_CAPABILITIES) break; @@ -1890,88 +1489,87 @@ void EmbeddedNetworkController::_request(uint64_t nwid, const InetAddress& fromA } } - std::map memberTagsById; + std::map< uint32_t,uint32_t > memberTagsById; if (memberTags.is_array()) { - for (unsigned long i = 0; i < memberTags.size(); ++i) { - json& t = memberTags[i]; - if ((t.is_array()) && (t.size() == 2)) - memberTagsById[(uint32_t)(OSUtils::jsonInt(t[0], 0ULL) & 0xffffffffULL)] = (uint32_t)(OSUtils::jsonInt(t[1], 0ULL) & 0xffffffffULL); + for(unsigned long i=0;i::const_iterator t(memberTagsById.begin()); t != memberTagsById.end(); ++t) { + for(std::map< uint32_t,uint32_t >::const_iterator t(memberTagsById.begin());t!=memberTagsById.end();++t) { if (nc->tagCount >= ZT_MAX_NETWORK_TAGS) break; - nc->tags[nc->tagCount] = Tag(nwid, now, identity.address(), t->first, t->second); + nc->tags[nc->tagCount] = Tag(nwid,now,identity.address(),t->first,t->second); if (nc->tags[nc->tagCount].sign(_signingId)) ++nc->tagCount; } } if (routes.is_array()) { - for (unsigned long i = 0; i < routes.size(); ++i) { + for(unsigned long i=0;irouteCount >= ZT_MAX_NETWORK_ROUTES) break; - json& route = routes[i]; - json& target = route["target"]; - json& via = route["via"]; + json &route = routes[i]; + json &target = route["target"]; + json &via = route["via"]; if (target.is_string()) { const InetAddress t(target.get().c_str()); InetAddress v; - if (via.is_string()) - v.fromString(via.get().c_str()); - if ((t.ss_family == AF_INET) || (t.ss_family == AF_INET6)) { - ZT_VirtualNetworkRoute* r = &(nc->routes[nc->routeCount]); - *(reinterpret_cast(&(r->target))) = t; + if (via.is_string()) v.fromString(via.get().c_str()); + if ((t.ss_family == AF_INET)||(t.ss_family == AF_INET6)) { + ZT_VirtualNetworkRoute *r = &(nc->routes[nc->routeCount]); + *(reinterpret_cast(&(r->target))) = t; if (v.ss_family == t.ss_family) - *(reinterpret_cast(&(r->via))) = v; + *(reinterpret_cast(&(r->via))) = v; ++nc->routeCount; } } } } - const bool noAutoAssignIps = OSUtils::jsonBool(member["noAutoAssignIps"], false); + const bool noAutoAssignIps = OSUtils::jsonBool(member["noAutoAssignIps"],false); - if ((v6AssignMode.is_object()) && (! noAutoAssignIps)) { - if ((OSUtils::jsonBool(v6AssignMode["rfc4193"], false)) && (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { - nc->staticIps[nc->staticIpCount++] = InetAddress::makeIpv6rfc4193(nwid, identity.address().toInt()); + if ((v6AssignMode.is_object())&&(!noAutoAssignIps)) { + if ((OSUtils::jsonBool(v6AssignMode["rfc4193"],false))&&(nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { + nc->staticIps[nc->staticIpCount++] = InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt()); nc->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; } - if ((OSUtils::jsonBool(v6AssignMode["6plane"], false)) && (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { - nc->staticIps[nc->staticIpCount++] = InetAddress::makeIpv66plane(nwid, identity.address().toInt()); + if ((OSUtils::jsonBool(v6AssignMode["6plane"],false))&&(nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { + nc->staticIps[nc->staticIpCount++] = InetAddress::makeIpv66plane(nwid,identity.address().toInt()); nc->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; } } bool haveManagedIpv4AutoAssignment = false; - bool haveManagedIpv6AutoAssignment = false; // "special" NDP-emulated address types do not count - json ipAssignments = member["ipAssignments"]; // we want to make a copy + bool haveManagedIpv6AutoAssignment = false; // "special" NDP-emulated address types do not count + json ipAssignments = member["ipAssignments"]; // we want to make a copy if (ipAssignments.is_array()) { - for (unsigned long i = 0; i < ipAssignments.size(); ++i) { + for(unsigned long i=0;irouteCount; ++rk) { - if (reinterpret_cast(&(nc->routes[rk].target))->containsAddress(ip)) { - const int nb = (int)(reinterpret_cast(&(nc->routes[rk].target))->netmaskBits()); + for(unsigned int rk=0;rkrouteCount;++rk) { + if (reinterpret_cast(&(nc->routes[rk].target))->containsAddress(ip)) { + const int nb = (int)(reinterpret_cast(&(nc->routes[rk].target))->netmaskBits()); if (nb > routedNetmaskBits) routedNetmaskBits = nb; } @@ -1989,21 +1587,20 @@ void EmbeddedNetworkController::_request(uint64_t nwid, const InetAddress& fromA } } } - } - else { + } else { ipAssignments = json::array(); } - if ((ipAssignmentPools.is_array()) && ((v6AssignMode.is_object()) && (OSUtils::jsonBool(v6AssignMode["zt"], false))) && (! haveManagedIpv6AutoAssignment) && (! noAutoAssignIps)) { - for (unsigned long p = 0; ((p < ipAssignmentPools.size()) && (! haveManagedIpv6AutoAssignment)); ++p) { - json& pool = ipAssignmentPools[p]; + if ( (ipAssignmentPools.is_array()) && ((v6AssignMode.is_object())&&(OSUtils::jsonBool(v6AssignMode["zt"],false))) && (!haveManagedIpv6AutoAssignment) && (!noAutoAssignIps) ) { + for(unsigned long p=0;((p s[1]) && ((e[1] - s[1]) >= 0xffffffffffULL)) { + for(unsigned int trialCount=0;trialCount<1000;++trialCount) { + if ((trialCount == 0)&&(e[1] > s[1])&&((e[1] - s[1]) >= 0xffffffffffULL)) { // First see if we can just cram a ZeroTier ID into the higher 64 bits. If so do that. xx[0] = Utils::hton(x[0]); xx[1] = Utils::hton(x[1] + identity.address().toInt()); - } - else { + } else { // Otherwise pick random addresses -- this technically doesn't explore the whole range if the lower 64 bit range is >= 1 but that won't matter since that would be huge anyway - Utils::getSecureRandom((void*)xx, 16); + Utils::getSecureRandom((void *)xx,16); if ((e[0] > s[0])) xx[0] %= (e[0] - s[0]); - else - xx[0] = 0; + else xx[0] = 0; if ((e[1] > s[1])) xx[1] %= (e[1] - s[1]); - else - xx[1] = 0; + else xx[1] = 0; xx[0] = Utils::hton(x[0] + xx[0]); xx[1] = Utils::hton(x[1] + xx[1]); } - InetAddress ip6((const void*)xx, 16, 0); + InetAddress ip6((const void *)xx,16,0); // Check if this IP is within a local-to-Ethernet routed network int routedNetmaskBits = 0; - for (unsigned int rk = 0; rk < nc->routeCount; ++rk) { - if ((! nc->routes[rk].via.ss_family) && (nc->routes[rk].target.ss_family == AF_INET6) && (reinterpret_cast(&(nc->routes[rk].target))->containsAddress(ip6))) - routedNetmaskBits = reinterpret_cast(&(nc->routes[rk].target))->netmaskBits(); + for(unsigned int rk=0;rkrouteCount;++rk) { + if ( (!nc->routes[rk].via.ss_family) && (nc->routes[rk].target.ss_family == AF_INET6) && (reinterpret_cast(&(nc->routes[rk].target))->containsAddress(ip6)) ) + routedNetmaskBits = reinterpret_cast(&(nc->routes[rk].target))->netmaskBits(); } // If it's routed, then try to claim and assign it and if successful end loop - if ((routedNetmaskBits > 0) && (! std::binary_search(ns.allocatedIps.begin(), ns.allocatedIps.end(), ip6))) { + if ( (routedNetmaskBits > 0) && (!std::binary_search(ns.allocatedIps.begin(),ns.allocatedIps.end(),ip6)) ) { char tmpip[64]; const std::string ipStr(ip6.toIpString(tmpip)); - if (std::find(ipAssignments.begin(), ipAssignments.end(), ipStr) == ipAssignments.end()) { + if (std::find(ipAssignments.begin(),ipAssignments.end(),ipStr) == ipAssignments.end()) { ipAssignments.push_back(ipStr); member["ipAssignments"] = ipAssignments; ip6.setPort((unsigned int)routedNetmaskBits); @@ -2061,36 +1655,36 @@ void EmbeddedNetworkController::_request(uint64_t nwid, const InetAddress& fromA } } - if ((ipAssignmentPools.is_array()) && ((v4AssignMode.is_object()) && (OSUtils::jsonBool(v4AssignMode["zt"], false))) && (! haveManagedIpv4AutoAssignment) && (! noAutoAssignIps)) { - for (unsigned long p = 0; ((p < ipAssignmentPools.size()) && (! haveManagedIpv4AutoAssignment)); ++p) { - json& pool = ipAssignmentPools[p]; + if ( (ipAssignmentPools.is_array()) && ((v4AssignMode.is_object())&&(OSUtils::jsonBool(v4AssignMode["zt"],false))) && (!haveManagedIpv4AutoAssignment) && (!noAutoAssignIps) ) { + for(unsigned long p=0;((p(&ipRangeStartIA)->sin_addr.s_addr)); - uint32_t ipRangeEnd = Utils::ntoh((uint32_t)(reinterpret_cast(&ipRangeEndIA)->sin_addr.s_addr)); + InetAddress ipRangeStartIA(OSUtils::jsonString(pool["ipRangeStart"],"").c_str()); + InetAddress ipRangeEndIA(OSUtils::jsonString(pool["ipRangeEnd"],"").c_str()); + if ( (ipRangeStartIA.ss_family == AF_INET) && (ipRangeEndIA.ss_family == AF_INET) ) { + uint32_t ipRangeStart = Utils::ntoh((uint32_t)(reinterpret_cast(&ipRangeStartIA)->sin_addr.s_addr)); + uint32_t ipRangeEnd = Utils::ntoh((uint32_t)(reinterpret_cast(&ipRangeEndIA)->sin_addr.s_addr)); - if ((ipRangeEnd < ipRangeStart) || (ipRangeStart == 0)) + if ((ipRangeEnd < ipRangeStart)||(ipRangeStart == 0)) continue; uint32_t ipRangeLen = ipRangeEnd - ipRangeStart; - + // Start with the LSB of the member's address uint32_t ipTrialCounter = (uint32_t)(identity.address().toInt() & 0xffffffff); - for (uint32_t k = ipRangeStart, trialCount = 0; ((k <= ipRangeEnd) && (trialCount < 1000)); ++k, ++trialCount) { + for(uint32_t k=ipRangeStart,trialCount=0;((k<=ipRangeEnd)&&(trialCount < 1000));++k,++trialCount) { uint32_t ip = (ipRangeLen > 0) ? (ipRangeStart + (ipTrialCounter % ipRangeLen)) : ipRangeStart; ++ipTrialCounter; if ((ip & 0x000000ff) == 0x000000ff) { - continue; // don't allow addresses that end in .255 + continue; // don't allow addresses that end in .255 } // Check if this IP is within a local-to-Ethernet routed network int routedNetmaskBits = -1; - for (unsigned int rk = 0; rk < nc->routeCount; ++rk) { + for(unsigned int rk=0;rkrouteCount;++rk) { if (nc->routes[rk].target.ss_family == AF_INET) { - uint32_t targetIp = Utils::ntoh((uint32_t)(reinterpret_cast(&(nc->routes[rk].target))->sin_addr.s_addr)); - int targetBits = Utils::ntoh((uint16_t)(reinterpret_cast(&(nc->routes[rk].target))->sin_port)); + uint32_t targetIp = Utils::ntoh((uint32_t)(reinterpret_cast(&(nc->routes[rk].target))->sin_addr.s_addr)); + int targetBits = Utils::ntoh((uint16_t)(reinterpret_cast(&(nc->routes[rk].target))->sin_port)); if ((ip & (0xffffffff << (32 - targetBits))) == targetIp) { routedNetmaskBits = targetBits; break; @@ -2099,15 +1693,15 @@ void EmbeddedNetworkController::_request(uint64_t nwid, const InetAddress& fromA } // If it's routed, then try to claim and assign it and if successful end loop - const InetAddress ip4(Utils::hton(ip), 0); - if ((routedNetmaskBits > 0) && (! std::binary_search(ns.allocatedIps.begin(), ns.allocatedIps.end(), ip4))) { + const InetAddress ip4(Utils::hton(ip),0); + if ( (routedNetmaskBits > 0) && (!std::binary_search(ns.allocatedIps.begin(),ns.allocatedIps.end(),ip4)) ) { char tmpip[64]; const std::string ipStr(ip4.toIpString(tmpip)); - if (std::find(ipAssignments.begin(), ipAssignments.end(), ipStr) == ipAssignments.end()) { + if (std::find(ipAssignments.begin(),ipAssignments.end(),ipStr) == ipAssignments.end()) { ipAssignments.push_back(ipStr); member["ipAssignments"] = ipAssignments; if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) { - struct sockaddr_in* const v4ip = reinterpret_cast(&(nc->staticIps[nc->staticIpCount++])); + struct sockaddr_in *const v4ip = reinterpret_cast(&(nc->staticIps[nc->staticIpCount++])); v4ip->sin_family = AF_INET; v4ip->sin_port = Utils::hton((uint16_t)routedNetmaskBits); v4ip->sin_addr.s_addr = Utils::hton(ip); @@ -2122,148 +1716,53 @@ void EmbeddedNetworkController::_request(uint64_t nwid, const InetAddress& fromA } } - if (dns.is_object()) { - std::string domain = OSUtils::jsonString(dns["domain"], ""); - memcpy(nc->dns.domain, domain.c_str(), domain.size()); - json& addrArray = dns["servers"]; - if (addrArray.is_array()) { - for (unsigned int j = 0; j < addrArray.size() && j < ZT_MAX_DNS_SERVERS; ++j) { - json& addr = addrArray[j]; - nc->dns.server_addr[j] = InetAddress(OSUtils::jsonString(addr, "").c_str()); - } - } - } - else { - dns = json::object(); - } -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b8.stop(); -#endif - // Issue a certificate of ownership for all static IPs -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - auto b9 = _issue_certificate.Add({ { "thread", threadID } }); - auto c9 = _issue_certificate_count.Add({ { "thread", threadID } }); - c9++; - b9.start(); -#endif if (nc->staticIpCount) { - nc->certificatesOfOwnership[0] = CertificateOfOwnership(nwid, now, identity.address(), 1); - for (unsigned int i = 0; i < nc->staticIpCount; ++i) { + nc->certificatesOfOwnership[0] = CertificateOfOwnership(nwid,now,identity.address(),1); + for(unsigned int i=0;istaticIpCount;++i) nc->certificatesOfOwnership[0].addThing(nc->staticIps[i]); - } nc->certificatesOfOwnership[0].sign(_signingId); nc->certificateOfOwnershipCount = 1; } - CertificateOfMembership com(now, credentialtmd, nwid, identity); + CertificateOfMembership com(now,credentialtmd,nwid,identity.address()); if (com.sign(_signingId)) { nc->com = com; - } - else { - _sender->ncSendError(nwid, requestPacketId, identity.address(), NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR, nullptr, 0); -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b9.stop(); -#endif + } else { + _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR); return; } -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b9.stop(); - auto b10 = _save_member.Add({ { "thread", threadID } }); - auto c10 = _save_member_count.Add({ { "thread", threadID } }); - c10++; - b10.start(); -#endif DB::cleanMember(member); - _db.save(member, true); -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b10.stop(); - - auto b11 = _send_netconf.Add({ { "thread", threadID } }); - auto c11 = _send_netconf_count.Add({ { "thread", threadID } }); - c11++; - b11.start(); -#endif - _sender->ncSendConfig(nwid, requestPacketId, identity.address(), *(nc.get()), metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION, 0) < 6); -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - b11.stop(); -#endif + _db->save(&origMember,member); + _sender->ncSendConfig(nwid,requestPacketId,identity.address(),*(nc.get()),metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6); } void EmbeddedNetworkController::_startThreads() { std::lock_guard l(_threads_l); - if (! _threads.empty()) { + if (!_threads.empty()) return; - } - const long hwc = std::max((long)std::thread::hardware_concurrency(), (long)1); - for (long t = 0; t < hwc; ++t) { + const long hwc = std::max((long)std::thread::hardware_concurrency(),(long)1); + for(long t=0;t::STOP) { + for(;;) { + _RQEntry *qe = (_RQEntry *)0; + if (!_queue.get(qe)) break; - } - else if (timedWaitResult == BlockingQueue<_RQEntry*>::OK) { + try { if (qe) { - try { - _request(qe->nwid, qe->fromAddr, qe->requestPacketId, qe->identity, qe->metaData); - } - catch (std::exception& e) { - fprintf(stderr, "ERROR: exception in controller request handling thread: %s" ZT_EOL_S, e.what()); - } - catch (...) { - fprintf(stderr, "ERROR: exception in controller request handling thread: unknown exception" ZT_EOL_S); - } + _request(qe->nwid,qe->fromAddr,qe->requestPacketId,qe->identity,qe->metaData); delete qe; - qe = nullptr; } + } catch (std::exception &e) { + fprintf(stderr,"ERROR: exception in controller request handling thread: %s" ZT_EOL_S,e.what()); + } catch ( ... ) { + fprintf(stderr,"ERROR: exception in controller request handling thread: unknown exception" ZT_EOL_S); } } - Metrics::network_config_request_threads--; }); } } -void EmbeddedNetworkController::_ssoExpiryThread() -{ - while (_ssoExpiryRunning) { - std::vector<_MemberStatusKey> expired; - nlohmann::json network, member; - int64_t now = OSUtils::now(); - { - std::lock_guard l(_expiringSoon_l); - for (auto s = _expiringSoon.begin(); s != _expiringSoon.end();) { - Metrics::sso_expiration_checks++; - const int64_t when = s->first; - if (when <= now) { - // The user may have re-authorized, so we must actually look it up and check. - network.clear(); - member.clear(); - if (_db.get(s->second.networkId, network, s->second.nodeId, member)) { - int64_t authenticationExpiryTime = (int64_t)OSUtils::jsonInt(member["authenticationExpiryTime"], 0); - if (authenticationExpiryTime <= now) { - expired.push_back(s->second); - } - } - s = _expiringSoon.erase(s); - } - else { - // Don't bother going further into the future than necessary. - break; - } - } - } - for (auto e = expired.begin(); e != expired.end(); ++e) { - Metrics::sso_member_deauth++; - onNetworkMemberDeauthorize(nullptr, e->networkId, e->nodeId); - } - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } -} - -} // namespace ZeroTier +} // namespace ZeroTier diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index 111417bfb..7bc37be21 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -1,122 +1,145 @@ /* - * Copyright (c)2019 ZeroTier, Inc. + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * Change Date: 2026-01-01 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ -/****/ #ifndef ZT_SQLITENETWORKCONTROLLER_HPP #define ZT_SQLITENETWORKCONTROLLER_HPP -#include "../node/Address.hpp" -#include "../node/Constants.hpp" -#include "../node/InetAddress.hpp" -#include "../node/NetworkController.hpp" -#include "../node/Utils.hpp" -#include "../osdep/BlockingQueue.hpp" -#include "../osdep/OSUtils.hpp" -#include "../osdep/Thread.hpp" -#include "DB.hpp" -#include "DBMirrorSet.hpp" - -#include -#include -#include -#include -#include -#include #include + #include +#include +#include +#include +#include #include #include -#include +#include + +#include "../node/Constants.hpp" +#include "../node/NetworkController.hpp" +#include "../node/Utils.hpp" +#include "../node/Address.hpp" +#include "../node/InetAddress.hpp" + +#include "../osdep/OSUtils.hpp" +#include "../osdep/Thread.hpp" +#include "../osdep/BlockingQueue.hpp" + +#include "../ext/json/json.hpp" + +#include "DB.hpp" namespace ZeroTier { class Node; -struct RedisConfig; -class EmbeddedNetworkController - : public NetworkController - , public DB::ChangeListener { - public: +struct MQConfig; + +class EmbeddedNetworkController : public NetworkController,public DB::ChangeListener +{ +public: /** * @param node Parent node * @param dbPath Database path (file path or database credentials) */ - EmbeddedNetworkController(Node* node, const char* ztPath, const char* dbPath, int listenPort, RedisConfig* rc); + EmbeddedNetworkController(Node *node,const char *dbPath, int listenPort, MQConfig *mqc = NULL); virtual ~EmbeddedNetworkController(); - virtual void init(const Identity& signingId, Sender* sender); + virtual void init(const Identity &signingId,Sender *sender); - void setSSORedirectURL(const std::string& url); + virtual void request( + uint64_t nwid, + const InetAddress &fromAddr, + uint64_t requestPacketId, + const Identity &identity, + const Dictionary &metaData); - virtual void request(uint64_t nwid, const InetAddress& fromAddr, uint64_t requestPacketId, const Identity& identity, const Dictionary& metaData); + unsigned int handleControlPlaneHttpGET( + const std::vector &path, + const std::map &urlArgs, + const std::map &headers, + const std::string &body, + std::string &responseBody, + std::string &responseContentType); + unsigned int handleControlPlaneHttpPOST( + const std::vector &path, + const std::map &urlArgs, + const std::map &headers, + const std::string &body, + std::string &responseBody, + std::string &responseContentType); + unsigned int handleControlPlaneHttpDELETE( + const std::vector &path, + const std::map &urlArgs, + const std::map &headers, + const std::string &body, + std::string &responseBody, + std::string &responseContentType); - void configureHTTPControlPlane(httplib::Server& s, httplib::Server& sV6, const std::function); + void handleRemoteTrace(const ZT_RemoteTrace &rt); - void handleRemoteTrace(const ZT_RemoteTrace& rt); + virtual void onNetworkUpdate(const uint64_t networkId,const nlohmann::json &network); + virtual void onNetworkMemberUpdate(const uint64_t networkId,const uint64_t memberId,const nlohmann::json &member); + virtual void onNetworkMemberDeauthorize(const uint64_t networkId,const uint64_t memberId); - virtual void onNetworkUpdate(const void* db, uint64_t networkId, const nlohmann::json& network); - virtual void onNetworkMemberUpdate(const void* db, uint64_t networkId, uint64_t memberId, const nlohmann::json& member); - virtual void onNetworkMemberDeauthorize(const void* db, uint64_t networkId, uint64_t memberId); - - private: - void _request(uint64_t nwid, const InetAddress& fromAddr, uint64_t requestPacketId, const Identity& identity, const Dictionary& metaData); +private: + void _request(uint64_t nwid,const InetAddress &fromAddr,uint64_t requestPacketId,const Identity &identity,const Dictionary &metaData); void _startThreads(); - void _ssoExpiryThread(); - std::string networkUpdateFromPostData(uint64_t networkID, const std::string& body); - - struct _RQEntry { + struct _RQEntry + { uint64_t nwid; uint64_t requestPacketId; InetAddress fromAddr; Identity identity; Dictionary metaData; - enum { RQENTRY_TYPE_REQUEST = 0 } type; + enum { + RQENTRY_TYPE_REQUEST = 0 + } type; }; - - struct _MemberStatusKey { - _MemberStatusKey() : networkId(0), nodeId(0) - { - } - _MemberStatusKey(const uint64_t nwid, const uint64_t nid) : networkId(nwid), nodeId(nid) - { - } + struct _MemberStatusKey + { + _MemberStatusKey() : networkId(0),nodeId(0) {} + _MemberStatusKey(const uint64_t nwid,const uint64_t nid) : networkId(nwid),nodeId(nid) {} uint64_t networkId; uint64_t nodeId; - inline bool operator==(const _MemberStatusKey& k) const - { - return ((k.networkId == networkId) && (k.nodeId == nodeId)); - } - inline bool operator<(const _MemberStatusKey& k) const - { - return (k.networkId < networkId) || ((k.networkId == networkId) && (k.nodeId < nodeId)); - } + inline bool operator==(const _MemberStatusKey &k) const { return ((k.networkId == networkId)&&(k.nodeId == nodeId)); } }; - struct _MemberStatus { - _MemberStatus() : lastRequestTime(0), authenticationExpiryTime(-1), vMajor(-1), vMinor(-1), vRev(-1), vProto(-1) - { - } - int64_t lastRequestTime; - int64_t authenticationExpiryTime; - int vMajor, vMinor, vRev, vProto; + struct _MemberStatus + { + _MemberStatus() : lastRequestTime(0),vMajor(-1),vMinor(-1),vRev(-1),vProto(-1) {} + uint64_t lastRequestTime; + int vMajor,vMinor,vRev,vProto; Dictionary lastRequestMetaData; Identity identity; - inline bool online(const int64_t now) const - { - return ((now - lastRequestTime) < (ZT_NETWORK_AUTOCONF_DELAY * 2)); - } + inline bool online(const int64_t now) const { return ((now - lastRequestTime) < (ZT_NETWORK_AUTOCONF_DELAY * 2)); } }; - struct _MemberStatusHash { - inline std::size_t operator()(const _MemberStatusKey& networkIdNodeId) const + struct _MemberStatusHash + { + inline std::size_t operator()(const _MemberStatusKey &networkIdNodeId) const { return (std::size_t)(networkIdNodeId.networkId + networkIdNodeId.nodeId); } @@ -124,57 +147,24 @@ class EmbeddedNetworkController const int64_t _startTime; int _listenPort; - Node* const _node; - std::string _ztPath; + Node *const _node; std::string _path; Identity _signingId; std::string _signingIdAddressString; - NetworkController::Sender* _sender; + NetworkController::Sender *_sender; - DBMirrorSet _db; - BlockingQueue<_RQEntry*> _queue; + std::unique_ptr _db; + BlockingQueue< _RQEntry * > _queue; std::vector _threads; std::mutex _threads_l; - std::unordered_map<_MemberStatusKey, _MemberStatus, _MemberStatusHash> _memberStatus; + std::unordered_map< _MemberStatusKey,_MemberStatus,_MemberStatusHash > _memberStatus; std::mutex _memberStatus_l; - std::set > _expiringSoon; - std::mutex _expiringSoon_l; - - RedisConfig* _rc; - std::string _ssoRedirectURL; - - bool _ssoExpiryRunning; - std::thread _ssoExpiry; - -#ifdef CENTRAL_CONTROLLER_REQUEST_BENCHMARK - prometheus::simpleapi::benchmark_family_t _member_status_lookup; - prometheus::simpleapi::counter_family_t _member_status_lookup_count; - prometheus::simpleapi::benchmark_family_t _node_is_online; - prometheus::simpleapi::counter_family_t _node_is_online_count; - prometheus::simpleapi::benchmark_family_t _get_and_init_member; - prometheus::simpleapi::counter_family_t _get_and_init_member_count; - prometheus::simpleapi::benchmark_family_t _have_identity; - prometheus::simpleapi::counter_family_t _have_identity_count; - prometheus::simpleapi::benchmark_family_t _determine_auth; - prometheus::simpleapi::counter_family_t _determine_auth_count; - prometheus::simpleapi::benchmark_family_t _sso_check; - prometheus::simpleapi::counter_family_t _sso_check_count; - prometheus::simpleapi::benchmark_family_t _auth_check; - prometheus::simpleapi::counter_family_t _auth_check_count; - prometheus::simpleapi::benchmark_family_t _json_schlep; - prometheus::simpleapi::counter_family_t _json_schlep_count; - prometheus::simpleapi::benchmark_family_t _issue_certificate; - prometheus::simpleapi::counter_family_t _issue_certificate_count; - prometheus::simpleapi::benchmark_family_t _save_member; - prometheus::simpleapi::counter_family_t _save_member_count; - prometheus::simpleapi::benchmark_family_t _send_netconf; - prometheus::simpleapi::counter_family_t _send_netconf_count; -#endif + MQConfig *_mqc; }; -} // namespace ZeroTier +} // namespace ZeroTier #endif diff --git a/controller/FileDB.cpp b/controller/FileDB.cpp index 2ccdad6ff..7b997c49a 100644 --- a/controller/FileDB.cpp +++ b/controller/FileDB.cpp @@ -1,65 +1,124 @@ /* - * Copyright (c)2019 ZeroTier, Inc. + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * Change Date: 2026-01-01 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ -/****/ #include "FileDB.hpp" -#include "../node/Metrics.hpp" +namespace ZeroTier +{ -namespace ZeroTier { - -FileDB::FileDB(const char* path) : DB(), _path(path), _networksPath(_path + ZT_PATH_SEPARATOR_S + "network"), _tracePath(_path + ZT_PATH_SEPARATOR_S + "trace"), _running(true) +FileDB::FileDB(const Identity &myId,const char *path) : + DB(myId,path), + _networksPath(_path + ZT_PATH_SEPARATOR_S + "network"), + _tracePath(_path + ZT_PATH_SEPARATOR_S + "trace"), + _onlineChanged(false), + _running(true) { OSUtils::mkdir(_path.c_str()); - OSUtils::lockDownFile(_path.c_str(), true); + OSUtils::lockDownFile(_path.c_str(),true); OSUtils::mkdir(_networksPath.c_str()); OSUtils::mkdir(_tracePath.c_str()); - std::vector networks(OSUtils::listDirectory(_networksPath.c_str(), false)); + std::vector networks(OSUtils::listDirectory(_networksPath.c_str(),false)); std::string buf; - for (auto n = networks.begin(); n != networks.end(); ++n) { + for(auto n=networks.begin();n!=networks.end();++n) { buf.clear(); - if ((n->length() == 21) && (OSUtils::readFile((_networksPath + ZT_PATH_SEPARATOR_S + *n).c_str(), buf))) { + if ((n->length() == 21)&&(OSUtils::readFile((_networksPath + ZT_PATH_SEPARATOR_S + *n).c_str(),buf))) { try { nlohmann::json network(OSUtils::jsonParse(buf)); const std::string nwids = network["id"]; if (nwids.length() == 16) { nlohmann::json nullJson; - _networkChanged(nullJson, network, false); - Metrics::network_count++; + _networkChanged(nullJson,network,false); std::string membersPath(_networksPath + ZT_PATH_SEPARATOR_S + nwids + ZT_PATH_SEPARATOR_S "member"); - std::vector members(OSUtils::listDirectory(membersPath.c_str(), false)); - for (auto m = members.begin(); m != members.end(); ++m) { + std::vector members(OSUtils::listDirectory(membersPath.c_str(),false)); + for(auto m=members.begin();m!=members.end();++m) { buf.clear(); - if ((m->length() == 15) && (OSUtils::readFile((membersPath + ZT_PATH_SEPARATOR_S + *m).c_str(), buf))) { + if ((m->length() == 15)&&(OSUtils::readFile((membersPath + ZT_PATH_SEPARATOR_S + *m).c_str(),buf))) { try { nlohmann::json member(OSUtils::jsonParse(buf)); const std::string addrs = member["id"]; if (addrs.length() == 10) { nlohmann::json nullJson2; - _memberChanged(nullJson2, member, false); - Metrics::member_count++; + _memberChanged(nullJson2,member,false); } - } - catch (...) { - } + } catch ( ... ) {} } } } - } - catch (...) { - } + } catch ( ... ) {} } } + + _onlineUpdateThread = std::thread([this]() { + unsigned int cnt = 0; + while (this->_running) { + std::this_thread::sleep_for(std::chrono::microseconds(100)); + if ((++cnt % 20) == 0) { // 5 seconds + std::lock_guard l(this->_online_l); + if (!this->_running) return; + if (this->_onlineChanged) { + char p[4096],atmp[64]; + for(auto nw=this->_online.begin();nw!=this->_online.end();++nw) { + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx-online.json",_networksPath.c_str(),(unsigned long long)nw->first); + FILE *f = fopen(p,"wb"); + if (f) { + fprintf(f,"{"); + const char *memberPrefix = ""; + for(auto m=nw->second.begin();m!=nw->second.end();++m) { + fprintf(f,"%s\"%.10llx\":{" ZT_EOL_S,memberPrefix,(unsigned long long)m->first); + memberPrefix = ","; + InetAddress lastAddr; + const char *timestampPrefix = " "; + int cnt = 0; + for(auto ts=m->second.rbegin();ts!=m->second.rend();) { + if (cnt < 25) { + if (lastAddr != ts->second) { + lastAddr = ts->second; + fprintf(f,"%s\"%lld\":\"%s\"" ZT_EOL_S,timestampPrefix,(long long)ts->first,ts->second.toString(atmp)); + timestampPrefix = ","; + ++cnt; + ++ts; + } else { + ts = std::map::reverse_iterator(m->second.erase(std::next(ts).base())); + } + } else { + ts = std::map::reverse_iterator(m->second.erase(std::next(ts).base())); + } + } + fprintf(f,"}"); + } + fprintf(f,"}" ZT_EOL_S); + fclose(f); + } + } + this->_onlineChanged = false; + } + } + } + }); } FileDB::~FileDB() @@ -69,109 +128,105 @@ FileDB::~FileDB() _running = false; _online_l.unlock(); _onlineUpdateThread.join(); - } - catch (...) { - } + } catch ( ... ) {} } -bool FileDB::waitForReady() -{ - return true; -} -bool FileDB::isReady() -{ - return true; -} +bool FileDB::waitForReady() { return true; } +bool FileDB::isReady() { return true; } -bool FileDB::save(nlohmann::json& record, bool notifyListeners) +void FileDB::save(nlohmann::json *orig,nlohmann::json &record) { - char p1[4096], p2[4096], pb[4096]; - bool modified = false; + char p1[4096],p2[4096],pb[4096]; try { + if (orig) { + if (*orig != record) { + record["revision"] = OSUtils::jsonInt(record["revision"],0ULL) + 1; + } + } else { + record["revision"] = 1; + } + const std::string objtype = record["objtype"]; if (objtype == "network") { - const uint64_t nwid = OSUtils::jsonIntHex(record["id"], 0ULL); + const uint64_t nwid = OSUtils::jsonIntHex(record["id"],0ULL); if (nwid) { nlohmann::json old; - get(nwid, old); - if ((! old.is_object()) || (! _compareRecords(old, record))) { - record["revision"] = OSUtils::jsonInt(record["revision"], 0ULL) + 1ULL; - OSUtils::ztsnprintf(p1, sizeof(p1), "%s" ZT_PATH_SEPARATOR_S "%.16llx.json", _networksPath.c_str(), nwid); - if (! OSUtils::writeFile(p1, OSUtils::jsonDump(record, -1))) { - fprintf(stderr, "WARNING: controller unable to write to path: %s" ZT_EOL_S, p1); - } - _networkChanged(old, record, notifyListeners); - modified = true; + get(nwid,old); + if ((!old.is_object())||(old != record)) { + OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%.16llx.json",_networksPath.c_str(),nwid); + if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1))) + fprintf(stderr,"WARNING: controller unable to write to path: %s" ZT_EOL_S,p1); + _networkChanged(old,record,true); } } - } - else if (objtype == "member") { - const uint64_t id = OSUtils::jsonIntHex(record["id"], 0ULL); - const uint64_t nwid = OSUtils::jsonIntHex(record["nwid"], 0ULL); - if ((id) && (nwid)) { - nlohmann::json network, old; - get(nwid, network, id, old); - if ((! old.is_object()) || (! _compareRecords(old, record))) { - record["revision"] = OSUtils::jsonInt(record["revision"], 0ULL) + 1ULL; - OSUtils::ztsnprintf(pb, sizeof(pb), "%s" ZT_PATH_SEPARATOR_S "%.16llx" ZT_PATH_SEPARATOR_S "member", _networksPath.c_str(), (unsigned long long)nwid); - OSUtils::ztsnprintf(p1, sizeof(p1), "%s" ZT_PATH_SEPARATOR_S "%.10llx.json", pb, (unsigned long long)id); - if (! OSUtils::writeFile(p1, OSUtils::jsonDump(record, -1))) { - OSUtils::ztsnprintf(p2, sizeof(p2), "%s" ZT_PATH_SEPARATOR_S "%.16llx", _networksPath.c_str(), (unsigned long long)nwid); + } else if (objtype == "member") { + const uint64_t id = OSUtils::jsonIntHex(record["id"],0ULL); + const uint64_t nwid = OSUtils::jsonIntHex(record["nwid"],0ULL); + if ((id)&&(nwid)) { + nlohmann::json network,old; + get(nwid,network,id,old); + if ((!old.is_object())||(old != record)) { + OSUtils::ztsnprintf(pb,sizeof(pb),"%s" ZT_PATH_SEPARATOR_S "%.16llx" ZT_PATH_SEPARATOR_S "member",_networksPath.c_str(),(unsigned long long)nwid); + OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%.10llx.json",pb,(unsigned long long)id); + if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1))) { + OSUtils::ztsnprintf(p2,sizeof(p2),"%s" ZT_PATH_SEPARATOR_S "%.16llx",_networksPath.c_str(),(unsigned long long)nwid); OSUtils::mkdir(p2); OSUtils::mkdir(pb); - if (! OSUtils::writeFile(p1, OSUtils::jsonDump(record, -1))) { - fprintf(stderr, "WARNING: controller unable to write to path: %s" ZT_EOL_S, p1); - } + if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1))) + fprintf(stderr,"WARNING: controller unable to write to path: %s" ZT_EOL_S,p1); } - _memberChanged(old, record, notifyListeners); - modified = true; + _memberChanged(old,record,true); } } + } else if (objtype == "trace") { + const std::string id = record["id"]; + if (id.length() > 0) { + OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%s.json",_tracePath.c_str(),id.c_str()); + OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1)); + } } - } - catch (...) { - } // drop invalid records missing fields - return modified; + } catch ( ... ) {} // drop invalid records missing fields } void FileDB::eraseNetwork(const uint64_t networkId) { - nlohmann::json network, nullJson; - get(networkId, network); + nlohmann::json network,nullJson; + get(networkId,network); char p[16384]; - OSUtils::ztsnprintf(p, sizeof(p), "%s" ZT_PATH_SEPARATOR_S "%.16llx.json", _networksPath.c_str(), networkId); + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.json",_networksPath.c_str(),networkId); OSUtils::rm(p); - OSUtils::ztsnprintf(p, sizeof(p), "%s" ZT_PATH_SEPARATOR_S "%.16llx", _networksPath.c_str(), (unsigned long long)networkId); + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx-online.json",_networksPath.c_str(),networkId); + OSUtils::rm(p); + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx" ZT_PATH_SEPARATOR_S "member",_networksPath.c_str(),(unsigned long long)networkId); OSUtils::rmDashRf(p); - _networkChanged(network, nullJson, true); + _networkChanged(network,nullJson,true); std::lock_guard l(this->_online_l); this->_online.erase(networkId); + this->_onlineChanged = true; } -void FileDB::eraseMember(const uint64_t networkId, const uint64_t memberId) +void FileDB::eraseMember(const uint64_t networkId,const uint64_t memberId) { - nlohmann::json network, member, nullJson; - get(networkId, network, memberId, member); + nlohmann::json network,member,nullJson; + get(networkId,network); + get(memberId,member); char p[4096]; - OSUtils::ztsnprintf(p, sizeof(p), "%s" ZT_PATH_SEPARATOR_S "%.16llx" ZT_PATH_SEPARATOR_S "member" ZT_PATH_SEPARATOR_S "%.10llx.json", _networksPath.c_str(), networkId, memberId); + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx" ZT_PATH_SEPARATOR_S "member" ZT_PATH_SEPARATOR_S "%.10llx.json",_networksPath.c_str(),networkId,memberId); OSUtils::rm(p); - _memberChanged(member, nullJson, true); + _memberChanged(member,nullJson,true); std::lock_guard l(this->_online_l); this->_online[networkId].erase(memberId); + this->_onlineChanged = true; } -void FileDB::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress, const char* osArch) +void FileDB::nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress) { - char mid[32], atmp[64]; - OSUtils::ztsnprintf(mid, sizeof(mid), "%.10llx", (unsigned long long)memberId); + char mid[32],atmp[64]; + OSUtils::ztsnprintf(mid,sizeof(mid),"%.10llx",(unsigned long long)memberId); physicalAddress.toString(atmp); std::lock_guard l(this->_online_l); this->_online[networkId][memberId][OSUtils::now()] = physicalAddress; + this->_onlineChanged = true; } -void FileDB::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress) -{ - this->nodeIsOnline(networkId, memberId, physicalAddress, "unknown/unknown"); -} - -} // namespace ZeroTier +} // namespace ZeroTier diff --git a/controller/FileDB.hpp b/controller/FileDB.hpp index e10753223..5d55d0a4a 100644 --- a/controller/FileDB.hpp +++ b/controller/FileDB.hpp @@ -1,46 +1,60 @@ /* - * Copyright (c)2019 ZeroTier, Inc. + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * Change Date: 2026-01-01 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ -/****/ #ifndef ZT_CONTROLLER_FILEDB_HPP #define ZT_CONTROLLER_FILEDB_HPP #include "DB.hpp" -namespace ZeroTier { +namespace ZeroTier +{ -class FileDB : public DB { - public: - FileDB(const char* path); +class FileDB : public DB +{ +public: + FileDB(const Identity &myId,const char *path); virtual ~FileDB(); virtual bool waitForReady(); virtual bool isReady(); - virtual bool save(nlohmann::json& record, bool notifyListeners); + virtual void save(nlohmann::json *orig,nlohmann::json &record); virtual void eraseNetwork(const uint64_t networkId); - virtual void eraseMember(const uint64_t networkId, const uint64_t memberId); - virtual void nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress); - virtual void nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress, const char* osArch); + virtual void eraseMember(const uint64_t networkId,const uint64_t memberId); + virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress); - protected: - std::string _path; +protected: std::string _networksPath; std::string _tracePath; std::thread _onlineUpdateThread; - std::map > > _online; + std::map< uint64_t,std::map > > _online; std::mutex _online_l; + bool _onlineChanged; bool _running; }; -} // namespace ZeroTier +} // namespace ZeroTier #endif diff --git a/controller/LFDB.cpp b/controller/LFDB.cpp index 77fa8ffe1..f0c8ebfb9 100644 --- a/controller/LFDB.cpp +++ b/controller/LFDB.cpp @@ -1,64 +1,77 @@ /* - * Copyright (c)2019 ZeroTier, Inc. + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * Change Date: 2026-01-01 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ -/****/ #include "LFDB.hpp" -#include "../ext/cpp-httplib/httplib.h" -#include "../osdep/OSUtils.hpp" - +#include #include #include #include -#include -namespace ZeroTier { +#include "../osdep/OSUtils.hpp" +#include "../ext/cpp-httplib/httplib.h" -LFDB::LFDB(const Identity& myId, const char* path, const char* lfOwnerPrivate, const char* lfOwnerPublic, const char* lfNodeHost, int lfNodePort, bool storeOnlineState) - : DB() - , _myId(myId) - , _lfOwnerPrivate((lfOwnerPrivate) ? lfOwnerPrivate : "") - , _lfOwnerPublic((lfOwnerPublic) ? lfOwnerPublic : "") - , _lfNodeHost((lfNodeHost) ? lfNodeHost : "127.0.0.1") - , _lfNodePort(((lfNodePort > 0) && (lfNodePort < 65536)) ? lfNodePort : 9980) - , _running(true) - , _ready(false) - , _storeOnlineState(storeOnlineState) +namespace ZeroTier +{ + +LFDB::LFDB(const Identity &myId,const char *path,const char *lfOwnerPrivate,const char *lfOwnerPublic,const char *lfNodeHost,int lfNodePort,bool storeOnlineState) : + DB(myId,path), + _myId(myId), + _lfOwnerPrivate((lfOwnerPrivate) ? lfOwnerPrivate : ""), + _lfOwnerPublic((lfOwnerPublic) ? lfOwnerPublic : ""), + _lfNodeHost((lfNodeHost) ? lfNodeHost : "127.0.0.1"), + _lfNodePort(((lfNodePort > 0)&&(lfNodePort < 65536)) ? lfNodePort : 9980), + _running(true), + _ready(false), + _storeOnlineState(storeOnlineState) { _syncThread = std::thread([this]() { char controllerAddress[24]; const uint64_t controllerAddressInt = _myId.address().toInt(); _myId.address().toString(controllerAddress); - std::string networksSelectorName("com.zerotier.controller.lfdb:"); - networksSelectorName.append(controllerAddress); - networksSelectorName.append("/network"); + std::string networksSelectorName("com.zerotier.controller.lfdb:"); networksSelectorName.append(controllerAddress); networksSelectorName.append("/network"); + std::string membersSelectorName("com.zerotier.controller.lfdb:"); membersSelectorName.append(controllerAddress); membersSelectorName.append("/member"); // LF record masking key is the first 32 bytes of SHA512(controller private key) in hex, // hiding record values from anything but the controller or someone who has its key. uint8_t sha512pk[64]; _myId.sha512PrivateKey(sha512pk); - char maskingKey[128]; - Utils::hex(sha512pk, 32, maskingKey); + char maskingKey [128]; + Utils::hex(sha512pk,32,maskingKey); - httplib::Client htcli(_lfNodeHost.c_str(), _lfNodePort); + httplib::Client htcli(_lfNodeHost.c_str(),_lfNodePort,600); int64_t timeRangeStart = 0; - while (_running.load()) { + while (_running) { { std::lock_guard sl(_state_l); - for (auto ns = _state.begin(); ns != _state.end(); ++ns) { + for(auto ns=_state.begin();ns!=_state.end();++ns) { if (ns->second.dirty) { nlohmann::json network; - if (get(ns->first, network)) { - nlohmann::json newrec, selector0; + if (get(ns->first,network)) { + nlohmann::json newrec,selector0; selector0["Name"] = networksSelectorName; selector0["Ordinal"] = ns->first; newrec["Selectors"].push_back(selector0); @@ -66,35 +79,25 @@ LFDB::LFDB(const Identity& myId, const char* path, const char* lfOwnerPrivate, c newrec["OwnerPrivate"] = _lfOwnerPrivate; newrec["MaskingKey"] = maskingKey; newrec["PulseIfUnchanged"] = true; - try { - auto resp = htcli.Post("/makerecord", newrec.dump(), "application/json"); - if (resp) { - if (resp->status == 200) { - ns->second.dirty = false; - // printf("SET network %.16llx %s\n",ns->first,resp->body.c_str()); - } - else { - fprintf(stderr, "ERROR: LFDB: %d from node (create/update network): %s" ZT_EOL_S, resp->status, resp->body.c_str()); - } + auto resp = htcli.Post("/makerecord",newrec.dump(),"application/json"); + if (resp) { + if (resp->status == 200) { + ns->second.dirty = false; + printf("SET network %.16llx %s\n",ns->first,resp->body.c_str()); + } else { + fprintf(stderr,"ERROR: LFDB: %d from node (create/update network): %s" ZT_EOL_S,resp->status,resp->body.c_str()); } - else { - fprintf(stderr, "ERROR: LFDB: node is offline" ZT_EOL_S); - } - } - catch (std::exception& e) { - fprintf(stderr, "ERROR: LFDB: unexpected exception querying node (create/update network): %s" ZT_EOL_S, e.what()); - } - catch (...) { - fprintf(stderr, "ERROR: LFDB: unexpected exception querying node (create/update network): unknown exception" ZT_EOL_S); + } else { + fprintf(stderr,"ERROR: LFDB: node is offline" ZT_EOL_S); } } } - for (auto ms = ns->second.members.begin(); ms != ns->second.members.end(); ++ms) { - if ((_storeOnlineState) && (ms->second.lastOnlineDirty) && (ms->second.lastOnlineAddress)) { - nlohmann::json newrec, selector0, selector1, selectors, ip; - char tmp[1024], tmp2[128]; - OSUtils::ztsnprintf(tmp, sizeof(tmp), "com.zerotier.controller.lfdb:%s/network/%.16llx/online", controllerAddress, (unsigned long long)ns->first); + for(auto ms=ns->second.members.begin();ms!=ns->second.members.end();++ms) { + if ((_storeOnlineState)&&(ms->second.lastOnlineDirty)&&(ms->second.lastOnlineAddress)) { + nlohmann::json newrec,selector0,selector1,selectors,ip; + char tmp[1024],tmp2[128]; + OSUtils::ztsnprintf(tmp,sizeof(tmp),"com.zerotier.controller.lfdb:%s/network/%.16llx/online",controllerAddress,(unsigned long long)ns->first); ms->second.lastOnlineAddress.toIpString(tmp2); selector0["Name"] = tmp; selector0["Ordinal"] = ms->first; @@ -103,18 +106,18 @@ LFDB::LFDB(const Identity& myId, const char* path, const char* lfOwnerPrivate, c selectors.push_back(selector0); selectors.push_back(selector1); newrec["Selectors"] = selectors; - const uint8_t* const rawip = (const uint8_t*)ms->second.lastOnlineAddress.rawIpData(); - switch (ms->second.lastOnlineAddress.ss_family) { + const uint8_t *const rawip = (const uint8_t *)ms->second.lastOnlineAddress.rawIpData(); + switch(ms->second.lastOnlineAddress.ss_family) { case AF_INET: - for (int j = 0; j < 4; ++j) + for(int j=0;j<4;++j) ip.push_back((unsigned int)rawip[j]); break; case AF_INET6: - for (int j = 0; j < 16; ++j) + for(int j=0;j<16;++j) ip.push_back((unsigned int)rawip[j]); break; default: - ip = tmp2; // should never happen since only IP transport is currently supported + ip = tmp2; // should never happen since only IP transport is currently supported break; } newrec["Value"] = ip; @@ -122,36 +125,26 @@ LFDB::LFDB(const Identity& myId, const char* path, const char* lfOwnerPrivate, c newrec["MaskingKey"] = maskingKey; newrec["Timestamp"] = ms->second.lastOnlineTime; newrec["PulseIfUnchanged"] = true; - try { - auto resp = htcli.Post("/makerecord", newrec.dump(), "application/json"); - if (resp) { - if (resp->status == 200) { - ms->second.lastOnlineDirty = false; - // printf("SET member online %.16llx %.10llx %s\n",ns->first,ms->first,resp->body.c_str()); - } - else { - fprintf(stderr, "ERROR: LFDB: %d from node (create/update member online status): %s" ZT_EOL_S, resp->status, resp->body.c_str()); - } + auto resp = htcli.Post("/makerecord",newrec.dump(),"application/json"); + if (resp) { + if (resp->status == 200) { + ms->second.lastOnlineDirty = false; + printf("SET member online %.16llx %.10llx %s\n",ns->first,ms->first,resp->body.c_str()); + } else { + fprintf(stderr,"ERROR: LFDB: %d from node (create/update member online status): %s" ZT_EOL_S,resp->status,resp->body.c_str()); } - else { - fprintf(stderr, "ERROR: LFDB: node is offline" ZT_EOL_S); - } - } - catch (std::exception& e) { - fprintf(stderr, "ERROR: LFDB: unexpected exception querying node (create/update member online status): %s" ZT_EOL_S, e.what()); - } - catch (...) { - fprintf(stderr, "ERROR: LFDB: unexpected exception querying node (create/update member online status): unknown exception" ZT_EOL_S); + } else { + fprintf(stderr,"ERROR: LFDB: node is offline" ZT_EOL_S); } } if (ms->second.dirty) { - nlohmann::json network, member; - if (get(ns->first, network, ms->first, member)) { - nlohmann::json newrec, selector0, selector1, selectors; + nlohmann::json network,member; + if (get(ns->first,network,ms->first,member)) { + nlohmann::json newrec,selector0,selector1,selectors; selector0["Name"] = networksSelectorName; selector0["Ordinal"] = ns->first; - selector1["Name"] = "member"; + selector1["Name"] = membersSelectorName; selector1["Ordinal"] = ms->first; selectors.push_back(selector0); selectors.push_back(selector1); @@ -160,26 +153,16 @@ LFDB::LFDB(const Identity& myId, const char* path, const char* lfOwnerPrivate, c newrec["OwnerPrivate"] = _lfOwnerPrivate; newrec["MaskingKey"] = maskingKey; newrec["PulseIfUnchanged"] = true; - try { - auto resp = htcli.Post("/makerecord", newrec.dump(), "application/json"); - if (resp) { - if (resp->status == 200) { - ms->second.dirty = false; - // printf("SET member %.16llx %.10llx %s\n",ns->first,ms->first,resp->body.c_str()); - } - else { - fprintf(stderr, "ERROR: LFDB: %d from node (create/update member): %s" ZT_EOL_S, resp->status, resp->body.c_str()); - } + auto resp = htcli.Post("/makerecord",newrec.dump(),"application/json"); + if (resp) { + if (resp->status == 200) { + ms->second.dirty = false; + printf("SET member %.16llx %.10llx %s\n",ns->first,ms->first,resp->body.c_str()); + } else { + fprintf(stderr,"ERROR: LFDB: %d from node (create/update member): %s" ZT_EOL_S,resp->status,resp->body.c_str()); } - else { - fprintf(stderr, "ERROR: LFDB: node is offline" ZT_EOL_S); - } - } - catch (std::exception& e) { - fprintf(stderr, "ERROR: LFDB: unexpected exception querying node (create/update member): %s" ZT_EOL_S, e.what()); - } - catch (...) { - fprintf(stderr, "ERROR: LFDB: unexpected exception querying node (create/update member): unknown exception" ZT_EOL_S); + } else { + fprintf(stderr,"ERROR: LFDB: node is offline" ZT_EOL_S); } } } @@ -187,57 +170,54 @@ LFDB::LFDB(const Identity& myId, const char* path, const char* lfOwnerPrivate, c } } - try { + { std::ostringstream query; - query << "{" - "\"Ranges\":[{" - "\"Name\":\"" - << networksSelectorName - << "\"," - "\"Range\":[0,18446744073709551615]" - "}]," - "\"TimeRange\":[" - << timeRangeStart - << ",9223372036854775807]," - "\"MaskingKey\":\"" - << maskingKey - << "\"," - "\"Owners\":[\"" - << _lfOwnerPublic - << "\"]" - "}"; - auto resp = htcli.Post("/query", query.str(), "application/json"); + query + << '{' + << "\"Ranges\":[{" + << "\"Name\":\"" << networksSelectorName << "\"," + << "\"Range\":[0,18446744073709551615]" + << "}]," + << "\"TimeRange\":[" << timeRangeStart << ",18446744073709551615]," + << "\"MaskingKey\":\"" << maskingKey << "\"," + << "\"Owners\":[\"" << _lfOwnerPublic << "\"]" + << '}'; + auto resp = htcli.Post("/query",query.str(),"application/json"); if (resp) { if (resp->status == 200) { nlohmann::json results(OSUtils::jsonParse(resp->body)); - if ((results.is_array()) && (! results.empty())) { - for (std::size_t ri = 0; ri < results.size(); ++ri) { - nlohmann::json& rset = results[ri]; - if ((rset.is_array()) && (! rset.empty())) { - nlohmann::json& result = rset[0]; + if ((results.is_array())&&(results.size() > 0)) { + for(std::size_t ri=0;ri 0)) { + nlohmann::json &result = rset[0]; if (result.is_object()) { - nlohmann::json& record = result["Record"]; + nlohmann::json &record = result["Record"]; if (record.is_object()) { const std::string recordValue = result["Value"]; - // printf("GET network %s\n",recordValue.c_str()); + printf("GET network %s\n",recordValue.c_str()); nlohmann::json network(OSUtils::jsonParse(recordValue)); if (network.is_object()) { const std::string idstr = network["id"]; const uint64_t id = Utils::hexStrToU64(idstr.c_str()); - if ((id >> 24) == controllerAddressInt) { // sanity check + if ((id >> 24) == controllerAddressInt) { // sanity check - nlohmann::json oldNetwork; - if ((timeRangeStart > 0) && (get(id, oldNetwork))) { - const uint64_t revision = network["revision"]; - const uint64_t prevRevision = oldNetwork["revision"]; - if (prevRevision < revision) { - _networkChanged(oldNetwork, network, timeRangeStart > 0); + std::lock_guard sl(_state_l); + _NetworkState &ns = _state[id]; + if (!ns.dirty) { + nlohmann::json oldNetwork; + if (get(id,oldNetwork)) { + const uint64_t revision = network["revision"]; + const uint64_t prevRevision = oldNetwork["revision"]; + if (prevRevision < revision) { + _networkChanged(oldNetwork,network,timeRangeStart > 0); + } + } else { + nlohmann::json nullJson; + _networkChanged(nullJson,network,timeRangeStart > 0); } } - else { - nlohmann::json nullJson; - _networkChanged(nullJson, network, timeRangeStart > 0); - } + } } } @@ -245,77 +225,66 @@ LFDB::LFDB(const Identity& myId, const char* path, const char* lfOwnerPrivate, c } } } + } else { + fprintf(stderr,"ERROR: LFDB: %d from node: %s" ZT_EOL_S,resp->status,resp->body.c_str()); } - else { - fprintf(stderr, "ERROR: LFDB: %d from node (check for network updates): %s" ZT_EOL_S, resp->status, resp->body.c_str()); - } + } else { + fprintf(stderr,"ERROR: LFDB: node is offline" ZT_EOL_S); } - else { - fprintf(stderr, "ERROR: LFDB: node is offline" ZT_EOL_S); - } - } - catch (std::exception& e) { - fprintf(stderr, "ERROR: LFDB: unexpected exception querying node (check for network updates): %s" ZT_EOL_S, e.what()); - } - catch (...) { - fprintf(stderr, "ERROR: LFDB: unexpected exception querying node (check for network updates): unknown exception" ZT_EOL_S); } - try { + { std::ostringstream query; - query << "{" - "\"Ranges\":[{" - "\"Name\":\"" - << networksSelectorName - << "\"," - "\"Range\":[0,18446744073709551615]" - "},{" - "\"Name\":\"member\"," - "\"Range\":[0,18446744073709551615]" - "}]," - "\"TimeRange\":[" - << timeRangeStart - << ",9223372036854775807]," - "\"MaskingKey\":\"" - << maskingKey - << "\"," - "\"Owners\":[\"" - << _lfOwnerPublic - << "\"]" - "}"; - auto resp = htcli.Post("/query", query.str(), "application/json"); + query + << '{' + << "\"Ranges\":[{" + << "\"Name\":\"" << networksSelectorName << "\"," + << "\"Range\":[0,18446744073709551615]" + << "},{" + << "\"Name\":\"" << membersSelectorName << "\"," + << "\"Range\":[0,18446744073709551615]" + << "}]," + << "\"TimeRange\":[" << timeRangeStart << ",18446744073709551615]," + << "\"MaskingKey\":\"" << maskingKey << "\"," + << "\"Owners\":[\"" << _lfOwnerPublic << "\"]" + << '}'; + auto resp = htcli.Post("/query",query.str(),"application/json"); if (resp) { if (resp->status == 200) { nlohmann::json results(OSUtils::jsonParse(resp->body)); - if ((results.is_array()) && (! results.empty())) { - for (std::size_t ri = 0; ri < results.size(); ++ri) { - nlohmann::json& rset = results[ri]; - if ((rset.is_array()) && (! rset.empty())) { - nlohmann::json& result = rset[0]; + if ((results.is_array())&&(results.size() > 0)) { + for(std::size_t ri=0;ri 0)) { + nlohmann::json &result = rset[0]; if (result.is_object()) { - nlohmann::json& record = result["Record"]; + nlohmann::json &record = result["Record"]; if (record.is_object()) { const std::string recordValue = result["Value"]; - // printf("GET member %s\n",recordValue.c_str()); + printf("GET member %s\n",recordValue.c_str()); nlohmann::json member(OSUtils::jsonParse(recordValue)); if (member.is_object()) { const std::string nwidstr = member["nwid"]; const std::string idstr = member["id"]; const uint64_t nwid = Utils::hexStrToU64(nwidstr.c_str()); const uint64_t id = Utils::hexStrToU64(idstr.c_str()); - if ((id) && ((nwid >> 24) == controllerAddressInt)) { // sanity check + if ((id)&&((nwid >> 24) == controllerAddressInt)) { // sanity check - nlohmann::json network, oldMember; - if ((timeRangeStart > 0) && (get(nwid, network, id, oldMember))) { - const uint64_t revision = member["revision"]; - const uint64_t prevRevision = oldMember["revision"]; - if (prevRevision < revision) - _memberChanged(oldMember, member, timeRangeStart > 0); - } - else if (hasNetwork(nwid)) { + std::lock_guard sl(_state_l); + auto ns = _state.find(nwid); + if ((ns == _state.end())||(!ns->second.members[id].dirty)) { + nlohmann::json network,oldMember; + if (get(nwid,network,id,oldMember)) { + const uint64_t revision = member["revision"]; + const uint64_t prevRevision = oldMember["revision"]; + if (prevRevision < revision) + _memberChanged(oldMember,member,timeRangeStart > 0); + } + } else { nlohmann::json nullJson; - _memberChanged(nullJson, member, timeRangeStart > 0); + _memberChanged(nullJson,member,timeRangeStart > 0); } + } } } @@ -323,29 +292,21 @@ LFDB::LFDB(const Identity& myId, const char* path, const char* lfOwnerPrivate, c } } } + } else { + fprintf(stderr,"ERROR: LFDB: %d from node: %s" ZT_EOL_S,resp->status,resp->body.c_str()); } - else { - fprintf(stderr, "ERROR: LFDB: %d from node (check for member updates): %s" ZT_EOL_S, resp->status, resp->body.c_str()); - } - } - else { - fprintf(stderr, "ERROR: LFDB: node is offline" ZT_EOL_S); + } else { + fprintf(stderr,"ERROR: LFDB: node is offline" ZT_EOL_S); } } - catch (std::exception& e) { - fprintf(stderr, "ERROR: LFDB: unexpected exception querying node (check for member updates): %s" ZT_EOL_S, e.what()); - } - catch (...) { - fprintf(stderr, "ERROR: LFDB: unexpected exception querying node (check for member updates): unknown exception" ZT_EOL_S); - } - timeRangeStart = time(nullptr) - 120; // start next query 2m before now to avoid losing updates - _ready.store(true); + timeRangeStart = time(nullptr) - 120; // start next query 2m before now to avoid losing updates + _ready = true; - for (int k = 0; k < 4; ++k) { // 2s delay between queries for remotely modified networks or members - if (! _running.load()) + for(int k=0;k<20;++k) { // 2s delay between queries for remotely modified networks or members + if (!_running) return; - std::this_thread::sleep_for(std::chrono::milliseconds(500)); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } }); @@ -353,61 +314,62 @@ LFDB::LFDB(const Identity& myId, const char* path, const char* lfOwnerPrivate, c LFDB::~LFDB() { - _running.store(false); + _running = false; _syncThread.join(); } bool LFDB::waitForReady() { - while (! _ready.load()) { - std::this_thread::sleep_for(std::chrono::milliseconds(500)); + while (!_ready) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } return true; } bool LFDB::isReady() { - return (_ready.load()); + return (_ready); } -bool LFDB::save(nlohmann::json& record, bool notifyListeners) +void LFDB::save(nlohmann::json *orig,nlohmann::json &record) { - bool modified = false; + if (orig) { + if (*orig != record) { + record["revision"] = OSUtils::jsonInt(record["revision"],0ULL) + 1; + } + } else { + record["revision"] = 1; + } + const std::string objtype = record["objtype"]; if (objtype == "network") { - const uint64_t nwid = OSUtils::jsonIntHex(record["id"], 0ULL); + const uint64_t nwid = OSUtils::jsonIntHex(record["id"],0ULL); if (nwid) { nlohmann::json old; - get(nwid, old); - if ((! old.is_object()) || (! _compareRecords(old, record))) { - record["revision"] = OSUtils::jsonInt(record["revision"], 0ULL) + 1ULL; - _networkChanged(old, record, notifyListeners); + get(nwid,old); + if ((!old.is_object())||(old != record)) { + _networkChanged(old,record,true); { std::lock_guard l(_state_l); _state[nwid].dirty = true; } - modified = true; } } - } - else if (objtype == "member") { - const uint64_t nwid = OSUtils::jsonIntHex(record["nwid"], 0ULL); - const uint64_t id = OSUtils::jsonIntHex(record["id"], 0ULL); - if ((id) && (nwid)) { - nlohmann::json network, old; - get(nwid, network, id, old); - if ((! old.is_object()) || (! _compareRecords(old, record))) { - record["revision"] = OSUtils::jsonInt(record["revision"], 0ULL) + 1ULL; - _memberChanged(old, record, notifyListeners); + } else if (objtype == "member") { + const uint64_t nwid = OSUtils::jsonIntHex(record["nwid"],0ULL); + const uint64_t id = OSUtils::jsonIntHex(record["id"],0ULL); + if ((id)&&(nwid)) { + nlohmann::json network,old; + get(nwid,network,id,old); + if ((!old.is_object())||(old != record)) { + _memberChanged(old,record,true); { std::lock_guard l(_state_l); _state[nwid].members[id].dirty = true; } - modified = true; } } } - return modified; } void LFDB::eraseNetwork(const uint64_t networkId) @@ -415,12 +377,12 @@ void LFDB::eraseNetwork(const uint64_t networkId) // TODO } -void LFDB::eraseMember(const uint64_t networkId, const uint64_t memberId) +void LFDB::eraseMember(const uint64_t networkId,const uint64_t memberId) { // TODO } -void LFDB::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress, const char* osArch) +void LFDB::nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress) { std::lock_guard l(_state_l); auto nw = _state.find(networkId); @@ -435,9 +397,4 @@ void LFDB::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const } } -void LFDB::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress) -{ - this->nodeIsOnline(networkId, memberId, physicalAddress, "unknown/unknown"); -} - -} // namespace ZeroTier +} // namespace ZeroTier diff --git a/controller/LFDB.hpp b/controller/LFDB.hpp index 3632e483f..731874626 100644 --- a/controller/LFDB.hpp +++ b/controller/LFDB.hpp @@ -1,35 +1,49 @@ /* - * Copyright (c)2019 ZeroTier, Inc. + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * Change Date: 2026-01-01 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ -/****/ #ifndef ZT_CONTROLLER_LFDB_HPP #define ZT_CONTROLLER_LFDB_HPP #include "DB.hpp" -#include #include #include #include +#include namespace ZeroTier { /** * DB implementation for controller that stores data in LF */ -class LFDB : public DB { - public: +class LFDB : public DB +{ +public: /** - * @param myId This controller's identity + * @param myId Identity of controller node (with secret) * @param path Base path for ZeroTier node itself * @param lfOwnerPrivate LF owner private in PEM format * @param lfOwnerPublic LF owner public in @base62 format @@ -37,41 +51,44 @@ class LFDB : public DB { * @param lfNodePort LF node http (not https) port * @param storeOnlineState If true, store online/offline state and IP info in LF (a lot of data, only for private networks!) */ - LFDB(const Identity& myId, const char* path, const char* lfOwnerPrivate, const char* lfOwnerPublic, const char* lfNodeHost, int lfNodePort, bool storeOnlineState); + LFDB(const Identity &myId,const char *path,const char *lfOwnerPrivate,const char *lfOwnerPublic,const char *lfNodeHost,int lfNodePort,bool storeOnlineState); virtual ~LFDB(); virtual bool waitForReady(); virtual bool isReady(); - virtual bool save(nlohmann::json& record, bool notifyListeners); + virtual void save(nlohmann::json *orig,nlohmann::json &record); virtual void eraseNetwork(const uint64_t networkId); - virtual void eraseMember(const uint64_t networkId, const uint64_t memberId); - virtual void nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress); - virtual void nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress& physicalAddress, const char* osArch); + virtual void eraseMember(const uint64_t networkId,const uint64_t memberId); + virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress); - protected: +protected: const Identity _myId; - std::string _lfOwnerPrivate, _lfOwnerPublic; + std::string _lfOwnerPrivate,_lfOwnerPublic; std::string _lfNodeHost; int _lfNodePort; - struct _MemberState { - _MemberState() : lastOnlineAddress(), lastOnlineTime(0), dirty(false), lastOnlineDirty(false) - { - } + struct _MemberState + { + _MemberState() : + lastOnlineAddress(), + lastOnlineTime(0), + dirty(false), + lastOnlineDirty(false) {} InetAddress lastOnlineAddress; int64_t lastOnlineTime; bool dirty; bool lastOnlineDirty; }; - struct _NetworkState { - _NetworkState() : members(), dirty(false) - { - } - std::unordered_map members; + struct _NetworkState + { + _NetworkState() : + members(), + dirty(false) {} + std::unordered_map members; bool dirty; }; - std::unordered_map _state; + std::unordered_map _state; std::mutex _state_l; std::atomic_bool _running; @@ -79,7 +96,7 @@ class LFDB : public DB { std::thread _syncThread; bool _storeOnlineState; }; - -} // namespace ZeroTier + +} // namespace ZeroTier #endif diff --git a/controller/PostgreSQL.cpp b/controller/PostgreSQL.cpp index a0ebff793..c6b9ada47 100644 --- a/controller/PostgreSQL.cpp +++ b/controller/PostgreSQL.cpp @@ -1,57 +1,1571 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + #ifdef ZT_CONTROLLER_USE_LIBPQ #include "PostgreSQL.hpp" +#include "EmbeddedNetworkController.hpp" +#include "RabbitMQ.hpp" +#include "../version.h" -#include +#include +#include +#include +#include -using namespace nlohmann; +using json = nlohmann::json; +namespace { + +static const int DB_MINIMUM_VERSION = 5; + +static const char *_timestr() +{ + time_t t = time(0); + char *ts = ctime(&t); + char *p = ts; + if (!p) + return ""; + while (*p) { + if (*p == '\n') { + *p = (char)0; + break; + } + ++p; + } + return ts; +} + +std::string join(const std::vector &elements, const char * const separator) +{ + switch(elements.size()) { + case 0: + return ""; + case 1: + return elements[0]; + default: + std::ostringstream os; + std::copy(elements.begin(), elements.end()-1, std::ostream_iterator(os, separator)); + os << *elements.rbegin(); + return os.str(); + } +} + +} using namespace ZeroTier; -MemberNotificationReceiver::MemberNotificationReceiver(DB* p, pqxx::connection& c, const std::string& channel) : pqxx::notification_receiver(c, channel), _psql(p) +PostgreSQL::PostgreSQL(const Identity &myId, const char *path, int listenPort, MQConfig *mqc) + : DB(myId, path) + , _ready(0) + , _connected(1) + , _run(1) + , _waitNoticePrinted(false) + , _listenPort(listenPort) + , _mqc(mqc) { - fprintf(stderr, "initialize MemberNotificationReceiver\n"); + _connString = std::string(path) + " application_name=controller_" +_myAddressStr; + + // Database Schema Version Check + PGconn *conn = getPgConn(); + if (PQstatus(conn) != CONNECTION_OK) { + fprintf(stderr, "Bad Database Connection: %s", PQerrorMessage(conn)); + exit(1); + } + + PGresult *res = PQexec(conn, "SELECT version FROM ztc_database"); + if (PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr, "Error determining database version"); + exit(1); + } + + if (PQntuples(res) != 1) { + fprintf(stderr, "Invalid number of db version tuples returned."); + exit(1); + } + + int dbVersion = std::stoi(PQgetvalue(res, 0, 0)); + + if (dbVersion < DB_MINIMUM_VERSION) { + fprintf(stderr, "Central database schema version too low. This controller version requires a minimum schema version of %d. Please upgrade your Central instance", DB_MINIMUM_VERSION); + exit(1); + } + + PQclear(res); + res = NULL; + PQfinish(conn); + conn = NULL; + + _readyLock.lock(); + _heartbeatThread = std::thread(&PostgreSQL::heartbeat, this); + _membersDbWatcher = std::thread(&PostgreSQL::membersDbWatcher, this); + _networksDbWatcher = std::thread(&PostgreSQL::networksDbWatcher, this); + for (int i = 0; i < ZT_CENTRAL_CONTROLLER_COMMIT_THREADS; ++i) { + _commitThread[i] = std::thread(&PostgreSQL::commitThread, this); + } + _onlineNotificationThread = std::thread(&PostgreSQL::onlineNotificationThread, this); } -void MemberNotificationReceiver::operator()(const std::string& payload, int packend_pid) +PostgreSQL::~PostgreSQL() { - fprintf(stderr, "Member Notification received: %s\n", payload.c_str()); - Metrics::pgsql_mem_notification++; - json tmp(json::parse(payload)); - json& ov = tmp["old_val"]; - json& nv = tmp["new_val"]; - json oldConfig, newConfig; - if (ov.is_object()) - oldConfig = ov; - if (nv.is_object()) - newConfig = nv; - if (oldConfig.is_object() || newConfig.is_object()) { - _psql->_memberChanged(oldConfig, newConfig, _psql->isReady()); - fprintf(stderr, "payload sent\n"); + _run = 0; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + _heartbeatThread.join(); + _membersDbWatcher.join(); + _networksDbWatcher.join(); + for (int i = 0; i < ZT_CENTRAL_CONTROLLER_COMMIT_THREADS; ++i) { + _commitThread[i].join(); + } + _onlineNotificationThread.join(); + +} + + +bool PostgreSQL::waitForReady() +{ + while (_ready < 2) { + if (!_waitNoticePrinted) { + _waitNoticePrinted = true; + fprintf(stderr, "[%s] NOTICE: %.10llx controller PostgreSQL waiting for initial data download..." ZT_EOL_S, ::_timestr(), (unsigned long long)_myAddress.toInt()); + } + _readyLock.lock(); + _readyLock.unlock(); + } + return true; +} + +bool PostgreSQL::isReady() +{ + return ((_ready == 2)&&(_connected)); +} + +void PostgreSQL::save(nlohmann::json *orig, nlohmann::json &record) +{ + try { + if (!record.is_object()) { + return; + } + waitForReady(); + if (orig) { + if (*orig != record) { + record["revision"] = OSUtils::jsonInt(record["revision"],0ULL) + 1; + _commitQueue.post(new nlohmann::json(record)); + } + } else { + record["revision"] = 1; + _commitQueue.post(new nlohmann::json(record)); + } + } catch (std::exception &e) { + fprintf(stderr, "Error on PostgreSQL::save: %s\n", e.what()); + } catch (...) { + fprintf(stderr, "Unknown error on PostgreSQL::save\n"); } } -NetworkNotificationReceiver::NetworkNotificationReceiver(DB* p, pqxx::connection& c, const std::string& channel) : pqxx::notification_receiver(c, channel), _psql(p) +void PostgreSQL::eraseNetwork(const uint64_t networkId) { - fprintf(stderr, "initialize NetworkNotificationReceiver\n"); + char tmp2[24]; + waitForReady(); + Utils::hex(networkId, tmp2); + json *tmp = new json(); + (*tmp)["id"] = tmp2; + (*tmp)["objtype"] = "_delete_network"; + _commitQueue.post(tmp); } -void NetworkNotificationReceiver::operator()(const std::string& payload, int packend_pid) +void PostgreSQL::eraseMember(const uint64_t networkId, const uint64_t memberId) { - fprintf(stderr, "Network Notification received: %s\n", payload.c_str()); - Metrics::pgsql_net_notification++; - json tmp(json::parse(payload)); - json& ov = tmp["old_val"]; - json& nv = tmp["new_val"]; - json oldConfig, newConfig; - if (ov.is_object()) - oldConfig = ov; - if (nv.is_object()) - newConfig = nv; - if (oldConfig.is_object() || newConfig.is_object()) { - _psql->_networkChanged(oldConfig, newConfig, _psql->isReady()); - fprintf(stderr, "payload sent\n"); + char tmp2[24]; + json *tmp = new json(); + Utils::hex(networkId, tmp2); + (*tmp)["nwid"] = tmp2; + Utils::hex(memberId, tmp2); + (*tmp)["id"] = tmp2; + (*tmp)["objtype"] = "_delete_member"; + _commitQueue.post(tmp); +} + +void PostgreSQL::nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress &physicalAddress) +{ + std::lock_guard l(_lastOnline_l); + std::pair &i = _lastOnline[std::pair(networkId, memberId)]; + i.first = OSUtils::now(); + if (physicalAddress) { + i.second = physicalAddress; } } -#endif \ No newline at end of file +void PostgreSQL::initializeNetworks(PGconn *conn) +{ + try { + if (PQstatus(conn) != CONNECTION_OK) { + fprintf(stderr, "Bad Database Connection: %s", PQerrorMessage(conn)); + exit(1); + } + + const char *params[1] = { + _myAddressStr.c_str() + }; + + PGresult *res = PQexecParams(conn, "SELECT id, EXTRACT(EPOCH FROM creation_time AT TIME ZONE 'UTC')*1000, capabilities, " + "enable_broadcast, EXTRACT(EPOCH FROM last_modified AT TIME ZONE 'UTC')*1000, mtu, multicast_limit, name, private, remote_trace_level, " + "remote_trace_target, revision, rules, tags, v4_assign_mode, v6_assign_mode FROM ztc_network " + "WHERE deleted = false AND controller_id = $1", + 1, + NULL, + params, + NULL, + NULL, + 0); + + if (PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr, "Networks Initialization Failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit(1); + } + + int numRows = PQntuples(res); + for (int i = 0; i < numRows; ++i) { + json empty; + json config; + + const char *nwidparam[1] = { + PQgetvalue(res, i, 0) + }; + + config["id"] = PQgetvalue(res, i, 0); + config["nwid"] = PQgetvalue(res, i, 0); + try { + config["creationTime"] = std::stoull(PQgetvalue(res, i, 1)); + } catch (std::exception &e) { + config["creationTime"] = 0ULL; + //fprintf(stderr, "Error converting creation time: %s\n", PQgetvalue(res, i, 1)); + } + config["capabilities"] = json::parse(PQgetvalue(res, i, 2)); + config["enableBroadcast"] = (strcmp(PQgetvalue(res, i, 3),"t")==0); + try { + config["lastModified"] = std::stoull(PQgetvalue(res, i, 4)); + } catch (std::exception &e) { + config["lastModified"] = 0ULL; + //fprintf(stderr, "Error converting last modified: %s\n", PQgetvalue(res, i, 4)); + } + try { + config["mtu"] = std::stoi(PQgetvalue(res, i, 5)); + } catch (std::exception &e) { + config["mtu"] = 2800; + } + try { + config["multicastLimit"] = std::stoi(PQgetvalue(res, i, 6)); + } catch (std::exception &e) { + config["multicastLimit"] = 64; + } + config["name"] = PQgetvalue(res, i, 7); + config["private"] = (strcmp(PQgetvalue(res, i, 8),"t")==0); + try { + config["remoteTraceLevel"] = std::stoi(PQgetvalue(res, i, 9)); + } catch (std::exception &e) { + config["remoteTraceLevel"] = 0; + } + config["remoteTraceTarget"] = PQgetvalue(res, i, 10); + try { + config["revision"] = std::stoull(PQgetvalue(res, i, 11)); + } catch (std::exception &e) { + config["revision"] = 0ULL; + //fprintf(stderr, "Error converting revision: %s\n", PQgetvalue(res, i, 11)); + } + config["rules"] = json::parse(PQgetvalue(res, i, 12)); + config["tags"] = json::parse(PQgetvalue(res, i, 13)); + config["v4AssignMode"] = json::parse(PQgetvalue(res, i, 14)); + config["v6AssignMode"] = json::parse(PQgetvalue(res, i, 15)); + config["objtype"] = "network"; + config["ipAssignmentPools"] = json::array(); + config["routes"] = json::array(); + + PGresult *r2 = PQexecParams(conn, + "SELECT host(ip_range_start), host(ip_range_end) FROM ztc_network_assignment_pool WHERE network_id = $1", + 1, + NULL, + nwidparam, + NULL, + NULL, + 0); + + if (PQresultStatus(r2) != PGRES_TUPLES_OK) { + fprintf(stderr, "ERROR: Error retreiving IP pools for network: %s\n", PQresultErrorMessage(r2)); + PQclear(r2); + PQclear(res); + exit(1); + } + + int n = PQntuples(r2); + for (int j = 0; j < n; ++j) { + json ip; + ip["ipRangeStart"] = PQgetvalue(r2, j, 0); + ip["ipRangeEnd"] = PQgetvalue(r2, j, 1); + + config["ipAssignmentPools"].push_back(ip); + } + + PQclear(r2); + + r2 = PQexecParams(conn, + "SELECT host(address), bits, host(via) FROM ztc_network_route WHERE network_id = $1", + 1, + NULL, + nwidparam, + NULL, + NULL, + 0); + + if (PQresultStatus(r2) != PGRES_TUPLES_OK) { + fprintf(stderr, "ERROR: Error retreiving routes for network: %s\n", PQresultErrorMessage(r2)); + PQclear(r2); + PQclear(res); + exit(1); + } + + n = PQntuples(r2); + for (int j = 0; j < n; ++j) { + std::string addr = PQgetvalue(r2, j, 0); + std::string bits = PQgetvalue(r2, j, 1); + std::string via = PQgetvalue(r2, j, 2); + json route; + route["target"] = addr + "/" + bits; + + if (via == "NULL") { + route["via"] = nullptr; + } else { + route["via"] = via; + } + config["routes"].push_back(route); + } + + PQclear(r2); + + _networkChanged(empty, config, false); + } + + PQclear(res); + + if (++this->_ready == 2) { + if (_waitNoticePrinted) { + fprintf(stderr,"[%s] NOTICE: %.10llx controller PostgreSQL data download complete." ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt()); + } + _readyLock.unlock(); + } + } catch (std::exception &e) { + fprintf(stderr, "ERROR: Error initializing networks: %s", e.what()); + exit(-1); + } +} + +void PostgreSQL::initializeMembers(PGconn *conn) +{ + try { + if (PQstatus(conn) != CONNECTION_OK) { + fprintf(stderr, "Bad Database Connection: %s", PQerrorMessage(conn)); + exit(1); + } + + const char *params[1] = { + _myAddressStr.c_str() + }; + + PGresult *res = PQexecParams(conn, + "SELECT m.id, m.network_id, m.active_bridge, m.authorized, m.capabilities, EXTRACT(EPOCH FROM m.creation_time AT TIME ZONE 'UTC')*1000, m.identity, " + " EXTRACT(EPOCH FROM m.last_authorized_time AT TIME ZONE 'UTC')*1000, " + " EXTRACT(EPOCH FROM m.last_deauthorized_time AT TIME ZONE 'UTC')*1000, " + " m.remote_trace_level, m.remote_trace_target, m.tags, m.v_major, m.v_minor, m.v_rev, m.v_proto, " + " m.no_auto_assign_ips, m.revision " + "FROM ztc_member m " + "INNER JOIN ztc_network n " + " ON n.id = m.network_id " + "WHERE n.controller_id = $1 AND m.deleted = false", + 1, + NULL, + params, + NULL, + NULL, + 0); + + if (PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr, "Member Initialization Failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit(1); + } + + int numRows = PQntuples(res); + for (int i = 0; i < numRows; ++i) { + json empty; + json config; + + std::string memberId(PQgetvalue(res, i, 0)); + std::string networkId(PQgetvalue(res, i, 1)); + std::string ctime = PQgetvalue(res, i, 5); + config["id"] = memberId; + config["nwid"] = networkId; + config["activeBridge"] = (strcmp(PQgetvalue(res, i, 2), "t") == 0); + config["authorized"] = (strcmp(PQgetvalue(res, i, 3), "t") == 0); + try { + config["capabilities"] = json::parse(PQgetvalue(res, i, 4)); + } catch (std::exception &e) { + config["capabilities"] = json::array(); + } + try { + config["creationTime"] = std::stoull(PQgetvalue(res, i, 5)); + } catch (std::exception &e) { + config["creationTime"] = 0ULL; + //fprintf(stderr, "Error upding creation time (member): %s\n", PQgetvalue(res, i, 5)); + } + config["identity"] = PQgetvalue(res, i, 6); + try { + config["lastAuthorizedTime"] = std::stoull(PQgetvalue(res, i, 7)); + } catch(std::exception &e) { + config["lastAuthorizedTime"] = 0ULL; + //fprintf(stderr, "Error updating last auth time (member): %s\n", PQgetvalue(res, i, 7)); + } + try { + config["lastDeauthorizedTime"] = std::stoull(PQgetvalue(res, i, 8)); + } catch( std::exception &e) { + config["lastDeauthorizedTime"] = 0ULL; + //fprintf(stderr, "Error updating last deauth time (member): %s\n", PQgetvalue(res, i, 8)); + } + try { + config["remoteTraceLevel"] = std::stoi(PQgetvalue(res, i, 9)); + } catch (std::exception &e) { + config["remoteTraceLevel"] = 0; + } + config["remoteTraceTarget"] = PQgetvalue(res, i, 10); + try { + config["tags"] = json::parse(PQgetvalue(res, i, 11)); + } catch (std::exception &e) { + config["tags"] = json::array(); + } + try { + config["vMajor"] = std::stoi(PQgetvalue(res, i, 12)); + } catch(std::exception &e) { + config["vMajor"] = -1; + } + try { + config["vMinor"] = std::stoi(PQgetvalue(res, i, 13)); + } catch (std::exception &e) { + config["vMinor"] = -1; + } + try { + config["vRev"] = std::stoi(PQgetvalue(res, i, 14)); + } catch (std::exception &e) { + config["vRev"] = -1; + } + try { + config["vProto"] = std::stoi(PQgetvalue(res, i, 15)); + } catch (std::exception &e) { + config["vProto"] = -1; + } + config["noAutoAssignIps"] = (strcmp(PQgetvalue(res, i, 16), "t") == 0); + try { + config["revision"] = std::stoull(PQgetvalue(res, i, 17)); + } catch (std::exception &e) { + config["revision"] = 0ULL; + //fprintf(stderr, "Error updating revision (member): %s\n", PQgetvalue(res, i, 17)); + } + config["objtype"] = "member"; + config["ipAssignments"] = json::array(); + const char *p2[2] = { + memberId.c_str(), + networkId.c_str() + }; + + PGresult *r2 = PQexecParams(conn, + "SELECT DISTINCT address FROM ztc_member_ip_assignment WHERE member_id = $1 AND network_id = $2", + 2, + NULL, + p2, + NULL, + NULL, + 0); + + if (PQresultStatus(r2) != PGRES_TUPLES_OK) { + fprintf(stderr, "Member Initialization Failed: %s", PQerrorMessage(conn)); + PQclear(r2); + PQclear(res); + exit(1); + } + + int n = PQntuples(r2); + for (int j = 0; j < n; ++j) { + config["ipAssignments"].push_back(PQgetvalue(r2, j, 0)); + } + + _memberChanged(empty, config, false); + } + + PQclear(res); + + if (++this->_ready == 2) { + if (_waitNoticePrinted) { + fprintf(stderr,"[%s] NOTICE: %.10llx controller PostgreSQL data download complete." ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt()); + } + _readyLock.unlock(); + } + } catch (std::exception &e) { + fprintf(stderr, "ERROR: Error initializing members: %s\n", e.what()); + exit(-1); + } +} + +void PostgreSQL::heartbeat() +{ + char publicId[1024]; + char hostnameTmp[1024]; + _myId.toString(false,publicId); + if (gethostname(hostnameTmp, sizeof(hostnameTmp))!= 0) { + hostnameTmp[0] = (char)0; + } else { + for (int i = 0; i < sizeof(hostnameTmp); ++i) { + if ((hostnameTmp[i] == '.')||(hostnameTmp[i] == 0)) { + hostnameTmp[i] = (char)0; + break; + } + } + } + const char *controllerId = _myAddressStr.c_str(); + const char *publicIdentity = publicId; + const char *hostname = hostnameTmp; + + PGconn *conn = getPgConn(); + if (PQstatus(conn) == CONNECTION_BAD) { + fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn)); + PQfinish(conn); + exit(1); + } + while (_run == 1) { + if(PQstatus(conn) != CONNECTION_OK) { + fprintf(stderr, "%s heartbeat thread lost connection to Database\n", _myAddressStr.c_str()); + PQfinish(conn); + exit(6); + } + if (conn) { + std::string major = std::to_string(ZEROTIER_ONE_VERSION_MAJOR); + std::string minor = std::to_string(ZEROTIER_ONE_VERSION_MINOR); + std::string rev = std::to_string(ZEROTIER_ONE_VERSION_REVISION); + std::string build = std::to_string(ZEROTIER_ONE_VERSION_BUILD); + std::string now = std::to_string(OSUtils::now()); + std::string host_port = std::to_string(_listenPort); + std::string use_rabbitmq = (_mqc != NULL) ? "true" : "false"; + const char *values[10] = { + controllerId, + hostname, + now.c_str(), + publicIdentity, + major.c_str(), + minor.c_str(), + rev.c_str(), + build.c_str(), + host_port.c_str(), + use_rabbitmq.c_str() + }; + + PGresult *res = PQexecParams(conn, + "INSERT INTO ztc_controller (id, cluster_host, last_alive, public_identity, v_major, v_minor, v_rev, v_build, host_port, use_rabbitmq) " + "VALUES ($1, $2, TO_TIMESTAMP($3::double precision/1000), $4, $5, $6, $7, $8, $9, $10) " + "ON CONFLICT (id) DO UPDATE SET cluster_host = EXCLUDED.cluster_host, last_alive = EXCLUDED.last_alive, " + "public_identity = EXCLUDED.public_identity, v_major = EXCLUDED.v_major, v_minor = EXCLUDED.v_minor, " + "v_rev = EXCLUDED.v_rev, v_build = EXCLUDED.v_rev, host_port = EXCLUDED.host_port, " + "use_rabbitmq = EXCLUDED.use_rabbitmq", + 10, // number of parameters + NULL, // oid field. ignore + values, // values for substitution + NULL, // lengths in bytes of each value + NULL, // binary? + 0); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "Heartbeat Update Failed: %s\n", PQresultErrorMessage(res)); + } + PQclear(res); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } + + PQfinish(conn); + conn = NULL; +} + +void PostgreSQL::membersDbWatcher() +{ + PGconn *conn = getPgConn(NO_OVERRIDE); + if (PQstatus(conn) == CONNECTION_BAD) { + fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn)); + PQfinish(conn); + exit(1); + } + + initializeMembers(conn); + + if (this->_mqc != NULL) { + PQfinish(conn); + conn = NULL; + _membersWatcher_RabbitMQ(); + } else { + _membersWatcher_Postgres(conn); + PQfinish(conn); + conn = NULL; + } + + if (_run == 1) { + fprintf(stderr, "ERROR: %s membersDbWatcher should still be running! Exiting Controller.\n", _myAddressStr.c_str()); + exit(9); + } + fprintf(stderr, "Exited membersDbWatcher\n"); +} + +void PostgreSQL::_membersWatcher_Postgres(PGconn *conn) { + char buf[11] = {0}; + std::string cmd = "LISTEN member_" + std::string(_myAddress.toString(buf)); + PGresult *res = PQexec(conn, cmd.c_str()); + if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "LISTEN command failed: %s\n", PQresultErrorMessage(res)); + PQclear(res); + PQfinish(conn); + exit(1); + } + + PQclear(res); res = NULL; + + while(_run == 1) { + if (PQstatus(conn) != CONNECTION_OK) { + fprintf(stderr, "ERROR: Member Watcher lost connection to Postgres."); + exit(-1); + } + PGnotify *notify = NULL; + PQconsumeInput(conn); + while ((notify = PQnotifies(conn)) != NULL) { + //fprintf(stderr, "ASYNC NOTIFY of '%s' id:%s received\n", notify->relname, notify->extra); + + try { + json tmp(json::parse(notify->extra)); + json &ov = tmp["old_val"]; + json &nv = tmp["new_val"]; + json oldConfig, newConfig; + if (ov.is_object()) oldConfig = ov; + if (nv.is_object()) newConfig = nv; + if (oldConfig.is_object() || newConfig.is_object()) { + _memberChanged(oldConfig,newConfig,(this->_ready>=2)); + } + } catch (...) {} // ignore bad records + + free(notify); + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } +} + +void PostgreSQL::_membersWatcher_RabbitMQ() { + char buf[11] = {0}; + std::string qname = "member_"+ std::string(_myAddress.toString(buf)); + RabbitMQ rmq(_mqc, qname.c_str()); + try { + rmq.init(); + } catch (std::runtime_error &e) { + fprintf(stderr, "RABBITMQ ERROR: %s\n", e.what()); + exit(11); + } + while (_run == 1) { + try { + std::string msg = rmq.consume(); + // fprintf(stderr, "Got Member Update: %s\n", msg.c_str()); + if (msg.empty()) { + continue; + } + json tmp(json::parse(msg)); + json &ov = tmp["old_val"]; + json &nv = tmp["new_val"]; + json oldConfig, newConfig; + if (ov.is_object()) oldConfig = ov; + if (nv.is_object()) newConfig = nv; + if (oldConfig.is_object() || newConfig.is_object()) { + _memberChanged(oldConfig,newConfig,(this->_ready>=2)); + } + } catch (std::runtime_error &e) { + fprintf(stderr, "RABBITMQ ERROR member change: %s\n", e.what()); + break; + } catch(std::exception &e ) { + fprintf(stderr, "RABBITMQ ERROR member change: %s\n", e.what()); + } catch(...) { + fprintf(stderr, "RABBITMQ ERROR member change: unknown error\n"); + } + } +} + +void PostgreSQL::networksDbWatcher() +{ + PGconn *conn = getPgConn(NO_OVERRIDE); + if (PQstatus(conn) == CONNECTION_BAD) { + fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn)); + PQfinish(conn); + exit(1); + } + + initializeNetworks(conn); + + if (this->_mqc != NULL) { + PQfinish(conn); + conn = NULL; + _networksWatcher_RabbitMQ(); + } else { + _networksWatcher_Postgres(conn); + PQfinish(conn); + conn = NULL; + } + + if (_run == 1) { + fprintf(stderr, "ERROR: %s networksDbWatcher should still be running! Exiting Controller.\n", _myAddressStr.c_str()); + exit(8); + } + fprintf(stderr, "Exited membersDbWatcher\n"); +} + +void PostgreSQL::_networksWatcher_Postgres(PGconn *conn) { + char buf[11] = {0}; + std::string cmd = "LISTEN network_" + std::string(_myAddress.toString(buf)); + PGresult *res = PQexec(conn, cmd.c_str()); + if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "LISTEN command failed: %s\n", PQresultErrorMessage(res)); + PQclear(res); + PQfinish(conn); + exit(1); + } + + PQclear(res); res = NULL; + + while(_run == 1) { + if (PQstatus(conn) != CONNECTION_OK) { + fprintf(stderr, "ERROR: Network Watcher lost connection to Postgres."); + exit(-1); + } + PGnotify *notify = NULL; + PQconsumeInput(conn); + while ((notify = PQnotifies(conn)) != NULL) { + //fprintf(stderr, "ASYNC NOTIFY of '%s' id:%s received\n", notify->relname, notify->extra); + try { + json tmp(json::parse(notify->extra)); + json &ov = tmp["old_val"]; + json &nv = tmp["new_val"]; + json oldConfig, newConfig; + if (ov.is_object()) oldConfig = ov; + if (nv.is_object()) newConfig = nv; + if (oldConfig.is_object()||newConfig.is_object()) { + _networkChanged(oldConfig,newConfig,(this->_ready >= 2)); + } + } catch (...) {} // ignore bad records + free(notify); + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } +} + +void PostgreSQL::_networksWatcher_RabbitMQ() { + char buf[11] = {0}; + std::string qname = "network_"+ std::string(_myAddress.toString(buf)); + RabbitMQ rmq(_mqc, qname.c_str()); + try { + rmq.init(); + } catch (std::runtime_error &e) { + fprintf(stderr, "RABBITMQ ERROR: %s\n", e.what()); + exit(11); + } + while (_run == 1) { + try { + std::string msg = rmq.consume(); + if (msg.empty()) { + continue; + } + // fprintf(stderr, "Got network update: %s\n", msg.c_str()); + json tmp(json::parse(msg)); + json &ov = tmp["old_val"]; + json &nv = tmp["new_val"]; + json oldConfig, newConfig; + if (ov.is_object()) oldConfig = ov; + if (nv.is_object()) newConfig = nv; + if (oldConfig.is_object()||newConfig.is_object()) { + _networkChanged(oldConfig,newConfig,(this->_ready >= 2)); + } + } catch (std::runtime_error &e) { + fprintf(stderr, "RABBITMQ ERROR: %s\n", e.what()); + break; + } catch (std::exception &e) { + fprintf(stderr, "RABBITMQ ERROR network watcher: %s\n", e.what()); + } catch(...) { + fprintf(stderr, "RABBITMQ ERROR network watcher: unknown error\n"); + } + } +} + +void PostgreSQL::commitThread() +{ + PGconn *conn = getPgConn(); + if (PQstatus(conn) == CONNECTION_BAD) { + fprintf(stderr, "ERROR: Connection to database failed: %s\n", PQerrorMessage(conn)); + PQfinish(conn); + exit(1); + } + + json *config = nullptr; + while(_commitQueue.get(config)&(_run == 1)) { + if (!config) { + continue; + } + if (PQstatus(conn) == CONNECTION_BAD) { + fprintf(stderr, "ERROR: Connection to database failed: %s\n", PQerrorMessage(conn)); + PQfinish(conn); + delete config; + exit(1); + } + try { + const std::string objtype = (*config)["objtype"]; + if (objtype == "member") { + try { + std::string memberId = (*config)["id"]; + std::string networkId = (*config)["nwid"]; + std::string identity = (*config)["identity"]; + std::string target = "NULL"; + + if (!(*config)["remoteTraceTarget"].is_null()) { + target = (*config)["remoteTraceTarget"]; + } + + std::string caps = OSUtils::jsonDump((*config)["capabilities"], -1); + std::string lastAuthTime = std::to_string((long long)(*config)["lastAuthorizedTime"]); + std::string lastDeauthTime = std::to_string((long long)(*config)["lastDeauthorizedTime"]); + std::string rtraceLevel = std::to_string((int)(*config)["remoteTraceLevel"]); + std::string rev = std::to_string((unsigned long long)(*config)["revision"]); + std::string tags = OSUtils::jsonDump((*config)["tags"], -1); + std::string vmajor = std::to_string((int)(*config)["vMajor"]); + std::string vminor = std::to_string((int)(*config)["vMinor"]); + std::string vrev = std::to_string((int)(*config)["vRev"]); + std::string vproto = std::to_string((int)(*config)["vProto"]); + const char *values[19] = { + memberId.c_str(), + networkId.c_str(), + ((*config)["activeBridge"] ? "true" : "false"), + ((*config)["authorized"] ? "true" : "false"), + caps.c_str(), + identity.c_str(), + lastAuthTime.c_str(), + lastDeauthTime.c_str(), + ((*config)["noAutoAssignIps"] ? "true" : "false"), + rtraceLevel.c_str(), + (target == "NULL") ? NULL : target.c_str(), + rev.c_str(), + tags.c_str(), + vmajor.c_str(), + vminor.c_str(), + vrev.c_str(), + vproto.c_str() + }; + + PGresult *res = PQexecParams(conn, + "INSERT INTO ztc_member (id, network_id, active_bridge, authorized, capabilities, " + "identity, last_authorized_time, last_deauthorized_time, no_auto_assign_ips, " + "remote_trace_level, remote_trace_target, revision, tags, v_major, v_minor, v_rev, v_proto) " + "VALUES ($1, $2, $3, $4, $5, $6, " + "TO_TIMESTAMP($7::double precision/1000), TO_TIMESTAMP($8::double precision/1000), " + "$9, $10, $11, $12, $13, $14, $15, $16, $17) ON CONFLICT (network_id, id) DO UPDATE SET " + "active_bridge = EXCLUDED.active_bridge, authorized = EXCLUDED.authorized, capabilities = EXCLUDED.capabilities, " + "identity = EXCLUDED.identity, last_authorized_time = EXCLUDED.last_authorized_time, " + "last_deauthorized_time = EXCLUDED.last_deauthorized_time, no_auto_assign_ips = EXCLUDED.no_auto_assign_ips, " + "remote_trace_level = EXCLUDED.remote_trace_level, remote_trace_target = EXCLUDED.remote_trace_target, " + "revision = EXCLUDED.revision+1, tags = EXCLUDED.tags, v_major = EXCLUDED.v_major, " + "v_minor = EXCLUDED.v_minor, v_rev = EXCLUDED.v_rev, v_proto = EXCLUDED.v_proto", + 17, + NULL, + values, + NULL, + NULL, + 0); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "ERROR: Error updating member: %s\n", PQresultErrorMessage(res)); + fprintf(stderr, "%s", OSUtils::jsonDump(*config, 2).c_str()); + PQclear(res); + delete config; + config = nullptr; + continue; + } + + PQclear(res); + + res = PQexec(conn, "BEGIN"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "ERROR: Error beginning transaction: %s\n", PQresultErrorMessage(res)); + PQclear(res); + delete config; + config = nullptr; + continue; + } + + PQclear(res); + + const char *v2[2] = { + memberId.c_str(), + networkId.c_str() + }; + + res = PQexecParams(conn, + "DELETE FROM ztc_member_ip_assignment WHERE member_id = $1 AND network_id = $2", + 2, + NULL, + v2, + NULL, + NULL, + 0); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "ERROR: Error updating IP address assignments: %s\n", PQresultErrorMessage(res)); + PQclear(res); + PQclear(PQexec(conn, "ROLLBACK"));; + delete config; + config = nullptr; + continue; + } + + PQclear(res); + + std::vector assignments; + for (auto i = (*config)["ipAssignments"].begin(); i != (*config)["ipAssignments"].end(); ++i) { + std::string addr = *i; + + if (std::find(assignments.begin(), assignments.end(), addr) != assignments.end()) { + continue; + } + + const char *v3[3] = { + memberId.c_str(), + networkId.c_str(), + addr.c_str() + }; + + res = PQexecParams(conn, + "INSERT INTO ztc_member_ip_assignment (member_id, network_id, address) VALUES ($1, $2, $3)", + 3, + NULL, + v3, + NULL, + NULL, + 0); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "ERROR: Error setting IP addresses for member: %s\n", PQresultErrorMessage(res)); + PQclear(res); + PQclear(PQexec(conn, "ROLLBACK")); + break;; + } + } + + res = PQexec(conn, "COMMIT"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "ERROR: Error committing ip address data: %s\n", PQresultErrorMessage(res)); + } + + PQclear(res); + + const uint64_t nwidInt = OSUtils::jsonIntHex((*config)["nwid"], 0ULL); + const uint64_t memberidInt = OSUtils::jsonIntHex((*config)["id"], 0ULL); + if (nwidInt && memberidInt) { + nlohmann::json nwOrig; + nlohmann::json memOrig; + + nlohmann::json memNew(*config); + + get(nwidInt, nwOrig, memberidInt, memOrig); + + _memberChanged(memOrig, memNew, (this->_ready>=2)); + } else { + fprintf(stderr, "Can't notify of change. Error parsing nwid or memberid: %lu-%lu\n", nwidInt, memberidInt); + } + + } catch (std::exception &e) { + fprintf(stderr, "ERROR: Error updating member: %s\n", e.what()); + } + } else if (objtype == "network") { + try { + std::string id = (*config)["id"]; + std::string controllerId = _myAddressStr.c_str(); + std::string name = (*config)["name"]; + std::string remoteTraceTarget("NULL"); + if (!(*config)["remoteTraceTarget"].is_null()) { + remoteTraceTarget = (*config)["remoteTraceTarget"]; + } + std::string rulesSource = (*config)["rulesSource"]; + std::string caps = OSUtils::jsonDump((*config)["capabilitles"], -1); + std::string now = std::to_string(OSUtils::now()); + std::string mtu = std::to_string((int)(*config)["mtu"]); + std::string mcastLimit = std::to_string((int)(*config)["multicastLimit"]); + std::string rtraceLevel = std::to_string((int)(*config)["remoteTraceLevel"]); + std::string rules = OSUtils::jsonDump((*config)["rules"], -1); + std::string tags = OSUtils::jsonDump((*config)["tags"], -1); + std::string v4mode = OSUtils::jsonDump((*config)["v4AssignMode"],-1); + std::string v6mode = OSUtils::jsonDump((*config)["v6AssignMode"], -1); + bool enableBroadcast = (*config)["enableBroadcast"]; + bool isPrivate = (*config)["private"]; + + const char *values[16] = { + id.c_str(), + controllerId.c_str(), + caps.c_str(), + enableBroadcast ? "true" : "false", + now.c_str(), + mtu.c_str(), + mcastLimit.c_str(), + name.c_str(), + isPrivate ? "true" : "false", + rtraceLevel.c_str(), + (remoteTraceTarget == "NULL" ? NULL : remoteTraceTarget.c_str()), + rules.c_str(), + rulesSource.c_str(), + tags.c_str(), + v4mode.c_str(), + v6mode.c_str(), + }; + + PGresult *res = PQexecParams(conn, + "UPDATE ztc_network SET controller_id = $2, capabilities = $3, enable_broadcast = $4, " + "last_updated = $5, mtu = $6, multicast_limit = $7, name = $8, private = $9, " + "remote_trace_level = $10, remote_trace_target = $11, rules = $12, rules_source = $13, " + "tags = $14, v4_assign_mode = $15, v6_assign_mode = $16 " + "WHERE id = $1", + 16, + NULL, + values, + NULL, + NULL, + 0); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "ERROR: Error updating network record: %s\n", PQresultErrorMessage(res)); + PQclear(res); + delete config; + config = nullptr; + continue; + } + + PQclear(res); + + res = PQexec(conn, "BEGIN"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "ERROR: Error beginnning transaction: %s\n", PQresultErrorMessage(res)); + PQclear(res); + delete config; + config = nullptr; + continue; + } + + PQclear(res); + + const char *params[1] = { + id.c_str() + }; + res = PQexecParams(conn, + "DELETE FROM ztc_network_assignment_pool WHERE network_id = $1", + 1, + NULL, + params, + NULL, + NULL, + 0); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "ERROR: Error updating assignment pool: %s\n", PQresultErrorMessage(res)); + PQclear(res); + PQclear(PQexec(conn, "ROLLBACK")); + delete config; + config = nullptr; + continue; + } + + PQclear(res); + + auto pool = (*config)["ipAssignmentPools"]; + bool err = false; + for (auto i = pool.begin(); i != pool.end(); ++i) { + std::string start = (*i)["ipRangeStart"]; + std::string end = (*i)["ipRangeEnd"]; + const char *p[3] = { + id.c_str(), + start.c_str(), + end.c_str() + }; + + res = PQexecParams(conn, + "INSERT INTO ztc_network_assignment_pool (network_id, ip_range_start, ip_range_end) " + "VALUES ($1, $2, $3)", + 3, + NULL, + p, + NULL, + NULL, + 0); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "ERROR: Error updating assignment pool: %s\n", PQresultErrorMessage(res)); + PQclear(res); + err = true; + break; + } + PQclear(res); + } + if (err) { + PQclear(PQexec(conn, "ROLLBACK")); + delete config; + config = nullptr; + continue; + } + + res = PQexecParams(conn, + "DELETE FROM ztc_network_route WHERE network_id = $1", + 1, + NULL, + params, + NULL, + NULL, + 0); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "ERROR: Error updating routes: %s\n", PQresultErrorMessage(res)); + PQclear(res); + PQclear(PQexec(conn, "ROLLBACK")); + delete config; + config = nullptr; + continue; + } + + + auto routes = (*config)["routes"]; + err = false; + for (auto i = routes.begin(); i != routes.end(); ++i) { + std::string t = (*i)["target"]; + std::vector target; + std::istringstream f(t); + std::string s; + while(std::getline(f, s, '/')) { + target.push_back(s); + } + if (target.empty() || target.size() != 2) { + continue; + } + std::string targetAddr = target[0]; + std::string targetBits = target[1]; + std::string via = "NULL"; + if (!(*i)["via"].is_null()) { + via = (*i)["via"]; + } + + const char *p[4] = { + id.c_str(), + targetAddr.c_str(), + targetBits.c_str(), + (via == "NULL" ? NULL : via.c_str()), + }; + + res = PQexecParams(conn, + "INSERT INTO ztc_network_route (network_id, address, bits, via) VALUES ($1, $2, $3, $4)", + 4, + NULL, + p, + NULL, + NULL, + 0); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "ERROR: Error updating routes: %s\n", PQresultErrorMessage(res)); + PQclear(res); + err = true; + break; + } + PQclear(res); + } + if (err) { + PQclear(PQexec(conn, "ROLLBACK")); + delete config; + config = nullptr; + continue; + } + + res = PQexec(conn, "COMMIT"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "ERROR: Error committing network update: %s\n", PQresultErrorMessage(res)); + } + PQclear(res); + + const uint64_t nwidInt = OSUtils::jsonIntHex((*config)["nwid"], 0ULL); + if (nwidInt) { + nlohmann::json nwOrig; + nlohmann::json nwNew(*config); + + get(nwidInt, nwOrig); + + _networkChanged(nwOrig, nwNew, true); + } else { + fprintf(stderr, "Can't notify network changed: %lu\n", nwidInt); + } + + } catch (std::exception &e) { + fprintf(stderr, "ERROR: Error updating member: %s\n", e.what()); + } + } else if (objtype == "trace") { + fprintf(stderr, "ERROR: Trace not yet implemented"); + } else if (objtype == "_delete_network") { + try { + std::string networkId = (*config)["nwid"]; + const char *values[1] = { + networkId.c_str() + }; + PGresult * res = PQexecParams(conn, + "UPDATE ztc_network SET deleted = true WHERE id = $1", + 1, + NULL, + values, + NULL, + NULL, + 0); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "ERROR: Error deleting network: %s\n", PQresultErrorMessage(res)); + } + + PQclear(res); + } catch (std::exception &e) { + fprintf(stderr, "ERROR: Error deleting network: %s\n", e.what()); + } + } else if (objtype == "_delete_member") { + try { + std::string memberId = (*config)["id"]; + std::string networkId = (*config)["nwid"]; + + const char *values[2] = { + memberId.c_str(), + networkId.c_str() + }; + + PGresult *res = PQexecParams(conn, + "UPDATE ztc_member SET hidden = true, deleted = true WHERE id = $1 AND network_id = $2", + 2, + NULL, + values, + NULL, + NULL, + 0); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "ERROR: Error deleting member: %s\n", PQresultErrorMessage(res)); + } + + PQclear(res); + } catch (std::exception &e) { + fprintf(stderr, "ERROR: Error deleting member: %s\n", e.what()); + } + } else { + fprintf(stderr, "ERROR: unknown objtype"); + } + } catch (std::exception &e) { + fprintf(stderr, "ERROR: Error getting objtype: %s\n", e.what()); + } + delete config; + config = nullptr; + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + PQfinish(conn); + if (_run == 1) { + fprintf(stderr, "ERROR: %s commitThread should still be running! Exiting Controller.\n", _myAddressStr.c_str()); + exit(7); + } +} + +void PostgreSQL::onlineNotificationThread() +{ + PGconn *conn = getPgConn(); + if (PQstatus(conn) == CONNECTION_BAD) { + fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn)); + PQfinish(conn); + exit(1); + } + _connected = 1; + + int64_t lastUpdatedNetworkStatus = 0; + std::unordered_map< std::pair,int64_t,_PairHasher > lastOnlineCumulative; + + while (_run == 1) { + if (PQstatus(conn) != CONNECTION_OK) { + fprintf(stderr, "ERROR: Online Notification thread lost connection to Postgres."); + PQfinish(conn); + exit(5); + } + + // map used to send notifications to front end + std::unordered_map> updateMap; + + std::unordered_map< std::pair,std::pair,_PairHasher > lastOnline; + { + std::lock_guard l(_lastOnline_l); + lastOnline.swap(_lastOnline); + } + + PGresult *res = NULL; + + std::stringstream memberUpdate; + memberUpdate << "INSERT INTO ztc_member_status (network_id, member_id, address, last_updated) VALUES "; + bool firstRun = true; + bool memberAdded = false; + for (auto i=lastOnline.begin(); i != lastOnline.end(); ++i) { + uint64_t nwid_i = i->first.first; + char nwidTmp[64]; + char memTmp[64]; + char ipTmp[64]; + OSUtils::ztsnprintf(nwidTmp,sizeof(nwidTmp), "%.16llx", nwid_i); + OSUtils::ztsnprintf(memTmp,sizeof(memTmp), "%.10llx", i->first.second); + + auto found = _networks.find(nwid_i); + if (found == _networks.end()) { + continue; // skip members trying to join non-existant networks + } + + std::string networkId(nwidTmp); + std::string memberId(memTmp); + + std::vector &members = updateMap[networkId]; + members.push_back(memberId); + + lastOnlineCumulative[i->first] = i->second.first; + + + const char *qvals[2] = { + networkId.c_str(), + memberId.c_str() + }; + + res = PQexecParams(conn, + "SELECT id, network_id FROM ztc_member WHERE network_id = $1 AND id = $2", + 2, + NULL, + qvals, + NULL, + NULL, + 0); + + if (PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr, "Member count failed: %s", PQerrorMessage(conn)); + PQclear(res); + continue; + } + + int nrows = PQntuples(res); + PQclear(res); + + if (nrows == 1) { + int64_t ts = i->second.first; + std::string ipAddr = i->second.second.toIpString(ipTmp); + std::string timestamp = std::to_string(ts); + + if (firstRun) { + firstRun = false; + } else { + memberUpdate << ", "; + } + + memberUpdate << "('" << networkId << "', '" << memberId << "', "; + if (ipAddr.empty()) { + memberUpdate << "NULL, "; + } else { + memberUpdate << "'" << ipAddr << "', "; + } + memberUpdate << "TO_TIMESTAMP(" << timestamp << "::double precision/1000))"; + memberAdded = true; + } else if (nrows > 1) { + fprintf(stderr, "nrows > 1?!?"); + continue; + } else { + continue; + } + } + memberUpdate << " ON CONFLICT (network_id, member_id) DO UPDATE SET address = EXCLUDED.address, last_updated = EXCLUDED.last_updated;"; + + if (memberAdded) { + res = PQexec(conn, memberUpdate.str().c_str()); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "Multiple insert failed: %s", PQerrorMessage(conn)); + } + PQclear(res); + } + + const int64_t now = OSUtils::now(); + if ((now - lastUpdatedNetworkStatus) > 10000) { + lastUpdatedNetworkStatus = now; + + std::vector>> networks; + { + std::lock_guard l(_networks_l); + for (auto i = _networks.begin(); i != _networks.end(); ++i) { + networks.push_back(*i); + } + } + + std::stringstream networkUpdate; + networkUpdate << "INSERT INTO ztc_network_status (network_id, bridge_count, authorized_member_count, online_member_count, total_member_count, last_modified) VALUES "; + bool nwFirstRun = true; + bool networkAdded = false; + for (auto i = networks.begin(); i != networks.end(); ++i) { + char tmp[64]; + Utils::hex(i->first, tmp); + + std::string networkId(tmp); + + std::vector &_notUsed = updateMap[networkId]; + (void)_notUsed; + + uint64_t authMemberCount = 0; + uint64_t totalMemberCount = 0; + uint64_t onlineMemberCount = 0; + uint64_t bridgeCount = 0; + uint64_t ts = now; + { + std::lock_guard l2(i->second->lock); + authMemberCount = i->second->authorizedMembers.size(); + totalMemberCount = i->second->members.size(); + bridgeCount = i->second->activeBridgeMembers.size(); + for (auto m=i->second->members.begin(); m != i->second->members.end(); ++m) { + auto lo = lastOnlineCumulative.find(std::pair(i->first, m->first)); + if (lo != lastOnlineCumulative.end()) { + if ((now - lo->second) <= (ZT_NETWORK_AUTOCONF_DELAY * 2)) { + ++onlineMemberCount; + } else { + lastOnlineCumulative.erase(lo); + } + } + } + } + + const char *nvals[1] = { + networkId.c_str() + }; + + res = PQexecParams(conn, + "SELECT id FROM ztc_network WHERE id = $1", + 1, + NULL, + nvals, + NULL, + NULL, + 0); + + if (PQresultStatus(res) != PGRES_TUPLES_OK) { + fprintf(stderr, "Network lookup failed: %s", PQerrorMessage(conn)); + PQclear(res); + continue; + } + + int nrows = PQntuples(res); + PQclear(res); + + if (nrows == 1) { + std::string bc = std::to_string(bridgeCount); + std::string amc = std::to_string(authMemberCount); + std::string omc = std::to_string(onlineMemberCount); + std::string tmc = std::to_string(totalMemberCount); + std::string timestamp = std::to_string(ts); + + if (nwFirstRun) { + nwFirstRun = false; + } else { + networkUpdate << ", "; + } + + networkUpdate << "('" << networkId << "', " << bc << ", " << amc << ", " << omc << ", " << tmc << ", " + << "TO_TIMESTAMP(" << timestamp << "::double precision/1000))"; + + networkAdded = true; + + } else if (nrows > 1) { + fprintf(stderr, "Number of networks > 1?!?!?"); + continue; + } else { + continue; + } + } + networkUpdate << " ON CONFLICT (network_id) DO UPDATE SET bridge_count = EXCLUDED.bridge_count, " + << "authorized_member_count = EXCLUDED.authorized_member_count, online_member_count = EXCLUDED.online_member_count, " + << "total_member_count = EXCLUDED.total_member_count, last_modified = EXCLUDED.last_modified"; + if (networkAdded) { + res = PQexec(conn, networkUpdate.str().c_str()); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + fprintf(stderr, "Error during multiple network upsert: %s", PQresultErrorMessage(res)); + } + PQclear(res); + } + } + + // for (auto it = updateMap.begin(); it != updateMap.end(); ++it) { + // std::string networkId = it->first; + // std::vector members = it->second; + // std::stringstream queryBuilder; + + // std::string membersStr = ::join(members, ","); + + // queryBuilder << "NOTIFY controller, '" << networkId << ":" << membersStr << "'"; + // std::string query = queryBuilder.str(); + + // PGresult *res = PQexec(conn,query.c_str()); + // if (PQresultStatus(res) != PGRES_COMMAND_OK) { + // fprintf(stderr, "ERROR: Error sending NOTIFY: %s\n", PQresultErrorMessage(res)); + // } + // PQclear(res); + // } + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + fprintf(stderr, "%s: Fell out of run loop in onlineNotificationThread\n", _myAddressStr.c_str()); + PQfinish(conn); + if (_run == 1) { + fprintf(stderr, "ERROR: %s onlineNotificationThread should still be running! Exiting Controller.\n", _myAddressStr.c_str()); + exit(6); + } +} + +PGconn *PostgreSQL::getPgConn(OverrideMode m) { + if (m == ALLOW_PGBOUNCER_OVERRIDE) { + char *connStr = getenv("PGBOUNCER_CONNSTR"); + if (connStr != NULL) { + fprintf(stderr, "PGBouncer Override\n"); + std::string conn(connStr); + conn += " application_name=controller-"; + conn += _myAddressStr.c_str(); + return PQconnectdb(conn.c_str()); + } + } + + return PQconnectdb(_connString.c_str()); +} +#endif //ZT_CONTROLLER_USE_LIBPQ diff --git a/controller/PostgreSQL.hpp b/controller/PostgreSQL.hpp index dc73405f5..f35f89fc9 100644 --- a/controller/PostgreSQL.hpp +++ b/controller/PostgreSQL.hpp @@ -1,97 +1,117 @@ /* - * Copyright (c)2025 ZeroTier, Inc. + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * Change Date: 2026-01-01 + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ -/****/ - + #ifdef ZT_CONTROLLER_USE_LIBPQ -#ifndef ZT_CONTROLLER_POSTGRESQL_HPP -#define ZT_CONTROLLER_POSTGRESQL_HPP +#ifndef ZT_CONTROLLER_LIBPQ_HPP +#define ZT_CONTROLLER_LIBPQ_HPP -#include "ConnectionPool.hpp" #include "DB.hpp" -#include -#include - -namespace ZeroTier { +#define ZT_CENTRAL_CONTROLLER_COMMIT_THREADS 4 extern "C" { -typedef struct pg_conn PGconn; + typedef struct pg_conn PGconn; } -class PostgresConnection : public Connection { - public: - virtual ~PostgresConnection() +namespace ZeroTier +{ + +struct MQConfig; + +/** + * A controller database driver that talks to PostgreSQL + * + * This is for use with ZeroTier Central. Others are free to build and use it + * but be aware taht we might change it at any time. + */ +class PostgreSQL : public DB +{ +public: + PostgreSQL(const Identity &myId, const char *path, int listenPort, MQConfig *mqc = NULL); + virtual ~PostgreSQL(); + + virtual bool waitForReady(); + virtual bool isReady(); + virtual void save(nlohmann::json *orig, nlohmann::json &record); + virtual void eraseNetwork(const uint64_t networkId); + virtual void eraseMember(const uint64_t networkId, const uint64_t memberId); + virtual void nodeIsOnline(const uint64_t networkId, const uint64_t memberId, const InetAddress &physicalAddress); + +protected: + struct _PairHasher { - } - - std::shared_ptr c; - int a; -}; - -class PostgresConnFactory : public ConnectionFactory { - public: - PostgresConnFactory(std::string& connString) : m_connString(connString) - { - } - - virtual std::shared_ptr create() - { - Metrics::conn_counter++; - auto c = std::shared_ptr(new PostgresConnection()); - c->c = std::make_shared(m_connString); - return std::static_pointer_cast(c); - } - - private: - std::string m_connString; -}; - -class MemberNotificationReceiver : public pqxx::notification_receiver { - public: - MemberNotificationReceiver(DB* p, pqxx::connection& c, const std::string& channel); - virtual ~MemberNotificationReceiver() - { - fprintf(stderr, "MemberNotificationReceiver destroyed\n"); - } - - virtual void operator()(const std::string& payload, int backendPid); - - private: - DB* _psql; -}; - -class NetworkNotificationReceiver : public pqxx::notification_receiver { - public: - NetworkNotificationReceiver(DB* p, pqxx::connection& c, const std::string& channel); - virtual ~NetworkNotificationReceiver() - { - fprintf(stderr, "NetworkNotificationReceiver destroyed\n"); + inline std::size_t operator()(const std::pair &p) const { return (std::size_t)(p.first ^ p.second); } }; - virtual void operator()(const std::string& payload, int packend_pid); +private: + void initializeNetworks(PGconn *conn); + void initializeMembers(PGconn *conn); + void heartbeat(); + void membersDbWatcher(); + void _membersWatcher_Postgres(PGconn *conn); + void _membersWatcher_RabbitMQ(); + void networksDbWatcher(); + void _networksWatcher_Postgres(PGconn *conn); + void _networksWatcher_RabbitMQ(); - private: - DB* _psql; + void commitThread(); + void onlineNotificationThread(); + + enum OverrideMode { + ALLOW_PGBOUNCER_OVERRIDE = 0, + NO_OVERRIDE = 1 + }; + + PGconn * getPgConn( OverrideMode m = ALLOW_PGBOUNCER_OVERRIDE ); + + std::string _connString; + + BlockingQueue _commitQueue; + + std::thread _heartbeatThread; + std::thread _membersDbWatcher; + std::thread _networksDbWatcher; + std::thread _commitThread[ZT_CENTRAL_CONTROLLER_COMMIT_THREADS]; + std::thread _onlineNotificationThread; + + std::unordered_map< std::pair,std::pair,_PairHasher > _lastOnline; + + mutable std::mutex _lastOnline_l; + mutable std::mutex _readyLock; + std::atomic _ready, _connected, _run; + mutable volatile bool _waitNoticePrinted; + + int _listenPort; + + MQConfig *_mqc; }; -struct NodeOnlineRecord { - uint64_t lastSeen; - InetAddress physicalAddress; - std::string osArch; -}; +} -} // namespace ZeroTier +#endif // ZT_CONTROLLER_LIBPQ_HPP -#endif // ZT_CONTROLLER_POSTGRESQL_HPP - -#endif // ZT_CONTROLLER_USE_LIBPQ \ No newline at end of file +#endif // ZT_CONTROLLER_USE_LIBPQ diff --git a/controller/README.md b/controller/README.md index 41cfd3ff3..c93c08f5a 100644 --- a/controller/README.md +++ b/controller/README.md @@ -3,7 +3,7 @@ Network Controller Microservice Every ZeroTier virtual network has a *network controller* responsible for admitting members to the network, issuing certificates, and issuing default configuration information. -This is our reference controller implementation and is almost the same as the one we use to power our own hosted services at [my.zerotier.com](https://my.zerotier.com/). The only difference is the database backend used. +This is our reference controller implementation and is the same one we use to power our own hosted services at [my.zerotier.com](https://my.zerotier.com/). As of ZeroTier One version 1.2.0 this code is included in normal builds for desktop, laptop, and server (Linux, etc.) targets. Controller data is stored in JSON format under `controller.d` in the ZeroTier working directory. It can be copied, rsync'd, placed in `git`, etc. The files under `controller.d` should not be modified in place while the controller is running or data loss may result, and if they are edited directly take care not to save corrupt JSON since that can also lead to data loss when the controller is restarted. Going through the API is strongly preferred to directly modifying these files. @@ -19,6 +19,10 @@ Since ZeroTier nodes are mobile and do not need static IPs, implementing high av ZeroTier network controllers can easily be run in Docker or other container systems. Since containers do not need to actually join networks, extra privilege options like "--device=/dev/net/tun --privileged" are not needed. You'll just need to map the local JSON API port of the running controller and allow it to access the Internet (over UDP/9993 at a minimum) so things can reach and query it. +### PostgreSQL Database Implementation + +The default controller stores its data in the filesystem in `controller.d` under ZeroTier's home folder. There's an alternative implementation that stores data in PostgreSQL that can be built with `make central-controller`. Right now this is only guaranteed to build and run on Centos 7 Linux with PostgreSQL 10 installed via the [PostgreSQL Yum Repository](https://www.postgresql.org/download/linux/redhat/) and is designed for use with [ZeroTier Central](https://my.zerotier.com/). You're welcome to use it but we don't "officially" support it for end-user use and it could change at any time. + ### Upgrading from Older (1.1.14 or earlier) Versions Older versions of this code used a SQLite database instead of in-filesystem JSON. A migration utility called `migrate-sqlite` is included here and *must* be used to migrate this data to the new format. If the controller is started with an old `controller.db` in its working directory it will terminate after printing an error to *stderr*. This is done to prevent "surprises" for those running DIY controllers using the old code. @@ -39,17 +43,191 @@ While networks with any valid ID can be added to the controller's database, it w The controller JSON API is *very* sensitive about types. Integers must be integers and strings strings, etc. Incorrect types may be ignored, set to default values, or set to undefined values. -Full documentation of the Controller API can be found on our [documentation site](https://docs.zerotier.com/service/v1#tag/controller) +#### `/controller` -### Prometheus Metrics + * Purpose: Check for controller function and return controller status + * Methods: GET + * Returns: { object } -Controller specific metrics are available from the `/metrics` endpoint. +| Field | Type | Description | Writable | +| ------------------ | ----------- | ------------------------------------------------- | -------- | +| controller | boolean | Always 'true' | no | +| apiVersion | integer | Controller API version, currently 3 | no | +| clock | integer | Current clock on controller, ms since epoch | no | + +#### `/controller/network` + + * Purpose: List all networks hosted by this controller + * Methods: GET + * Returns: [ string, ... ] + +This returns an array of 16-digit hexadecimal network IDs. + +#### `/controller/network/` + + * Purpose: Create, configure, and delete hosted networks + * Methods: GET, POST, DELETE + * Returns: { object } + +By making queries to this path you can create, configure, and delete networks. DELETE is final, so don't do it unless you really mean it. + +When POSTing new networks take care that their IDs are not in use, otherwise you may overwrite an existing one. To create a new network with a random unused ID, POST to `/controller/network/##########______`. The #'s are the controller's 10-digit ZeroTier address and they're followed by six underscores. Check the `nwid` field of the returned JSON object for your network's newly allocated ID. Subsequent POSTs to this network must refer to its actual path. + +Example: + +`curl -X POST --header "X-ZT1-Auth: secret" -d '{"name":"my network"}' http://localhost:9993/controller/network/305f406058______` + +**Network object format:** + +| Field | Type | Description | Writable | +| --------------------- | ------------- | ------------------------------------------------- | -------- | +| id | string | 16-digit network ID | no | +| nwid | string | 16-digit network ID (legacy) | no | +| objtype | string | Always "network" | no | +| name | string | A short name for this network | YES | +| creationTime | integer | Time network record was created (ms since epoch) | no | +| private | boolean | Is access control enabled? | YES | +| enableBroadcast | boolean | Ethernet ff:ff:ff:ff:ff:ff allowed? | YES | +| v4AssignMode | object | IPv4 management and assign options (see below) | YES | +| v6AssignMode | object | IPv6 management and assign options (see below) | YES | +| mtu | integer | Network MTU (default: 2800) | YES | +| multicastLimit | integer | Maximum recipients for a multicast packet | YES | +| creationTime | integer | Time network was first created | no | +| revision | integer | Network config revision counter | no | +| routes | array[object] | Managed IPv4 and IPv6 routes; see below | YES | +| ipAssignmentPools | array[object] | IP auto-assign ranges; see below | YES | +| rules | array[object] | Traffic rules; see below | YES | +| capabilities | array[object] | Array of capability objects (see below) | YES | +| tags | array[object] | Array of tag objects (see below) | YES | +| remoteTraceTarget | string | 10-digit ZeroTier ID of remote trace target | YES | +| remoteTraceLevel | integer | Remote trace verbosity level | YES | + + * Networks without rules won't carry any traffic. If you don't specify any on network creation an "accept anything" rule set will automatically be added. + * Managed IP address assignments and IP assignment pools that do not fall within a route configured in `routes` are ignored and won't be used or sent to members. + * The default for `private` is `true` and this is probably what you want. Turning `private` off means *anyone* can join your network with only its 16-digit network ID. It's also impossible to de-authorize a member as these networks don't issue or enforce certificates. Such "party line" networks are used for decentralized app backplanes, gaming, and testing but are otherwise not common. + * Changing the MTU can be disruptive and on some operating systems may require a leave/rejoin of the network or a restart of the ZeroTier service. + +**Auto-Assign Modes:** + +Auto assign modes (`v4AssignMode` and `v6AssignMode`) contain objects that map assignment modes to booleans. + +For IPv4 the only valid setting is `zt` which, if true, causes IPv4 addresses to be auto-assigned from `ipAssignmentPools` to members that do not have an IPv4 assignment. Note that active bridges are exempt and will not get auto-assigned IPs since this can interfere with bridging. (You can still manually assign one if you want.) + +IPv6 includes this option and two others: `6plane` and `rfc4193`. These assign private IPv6 addresses to each member based on a deterministic assignment scheme that allows members to emulate IPv6 NDP to skip multicast for better performance and scalability. The `rfc4193` mode gives every member a /128 on a /88 network, while `6plane` gives every member a /80 within a /40 network but uses NDP emulation to route *all* IPs under that /80 to its owner. The `6plane` mode is great for use cases like Docker since it allows every member to assign IPv6 addresses within its /80 that just work instantly and globally across the network. + +**IP assignment pool object format:** + +| Field | Type | Description | +| --------------------- | ------------- | ------------------------------------------------- | +| ipRangeStart | string | Starting IP address in range | +| ipRangeEnd | string | Ending IP address in range (inclusive) | + +Pools are only used if auto-assignment is on for the given address type (IPv4 or IPv6) and if the entire range falls within a managed route. + +IPv6 ranges work just like IPv4 ranges and look like this: + + { + "ipRangeStart": "fd00:feed:feed:beef:0000:0000:0000:0000", + "ipRangeEnd": "fd00:feed:feed:beef:ffff:ffff:ffff:ffff" + } + +(You can POST a shortened-form IPv6 address but the API will always report back un-shortened canonical form addresses.) + +That defines a range within network `fd00:feed:feed:beef::/64` that contains up to 2^64 addresses. If an IPv6 range is large enough, the controller will assign addresses by placing each member's device ID into the address in a manner similar to the RFC4193 and 6PLANE modes. Otherwise it will assign addresses at random. + +**Rule object format:** + +Each rule is actually a sequence of zero or more `MATCH_` entries in the rule array followed by an `ACTION_` entry that describes what to do if all the preceding entries match. An `ACTION_` without any preceding `MATCH_` entries is always taken, so setting a single `ACTION_ACCEPT` rule yields a network that allows all traffic. If no rules are present the default action is `ACTION_DROP`. + +Rules are evaluated in the order in which they appear in the array. There is currently a limit of 256 entries per network. Capabilities should be used if a larger and more complex rule set is needed since they allow rules to be grouped by purpose and only shipped to members that need them. + +Each rule table entry has two common fields. + +| Field | Type | Description | +| --------------------- | ------------- | ------------------------------------------------- | +| type | string | Entry type (all caps, case sensitive) | +| not | boolean | If true, MATCHes match if they don't match | + +The following fields may or may not be present depending on rule type: + +| Field | Type | Description | +| --------------------- | ------------- | ------------------------------------------------- | +| zt | string | 10-digit hex ZeroTier address | +| etherType | integer | Ethernet frame type | +| mac | string | Hex MAC address (with or without :'s) | +| ip | string | IPv4 or IPv6 address | +| ipTos | integer | IP type of service | +| ipProtocol | integer | IP protocol (e.g. TCP) | +| start | integer | Start of an integer range (e.g. port range) | +| end | integer | End of an integer range (inclusive) | +| id | integer | Tag ID | +| value | integer | Tag value or comparison value | +| mask | integer | Bit mask (for characteristics flags) | + +The entry types and their additional fields are: + +| Entry type | Description | Fields | +| ------------------------------- | ----------------------------------------------------------------- | -------------- | +| `ACTION_DROP` | Drop any packets matching this rule | (none) | +| `ACTION_ACCEPT` | Accept any packets matching this rule | (none) | +| `ACTION_TEE` | Send a copy of this packet to a node (rule parsing continues) | `zt` | +| `ACTION_REDIRECT` | Redirect this packet to another node | `zt` | +| `ACTION_DEBUG_LOG` | Output debug info on match (if built with rules engine debug) | (none) | +| `MATCH_SOURCE_ZEROTIER_ADDRESS` | Match VL1 ZeroTier address of packet sender. | `zt` | +| `MATCH_DEST_ZEROTIER_ADDRESS` | Match VL1 ZeroTier address of recipient | `zt` | +| `MATCH_ETHERTYPE` | Match Ethernet frame type | `etherType` | +| `MATCH_MAC_SOURCE` | Match source Ethernet MAC address | `mac` | +| `MATCH_MAC_DEST` | Match destination Ethernet MAC address | `mac` | +| `MATCH_IPV4_SOURCE` | Match source IPv4 address | `ip` | +| `MATCH_IPV4_DEST` | Match destination IPv4 address | `ip` | +| `MATCH_IPV6_SOURCE` | Match source IPv6 address | `ip` | +| `MATCH_IPV6_DEST` | Match destination IPv6 address | `ip` | +| `MATCH_IP_TOS` | Match IP TOS field | `ipTos` | +| `MATCH_IP_PROTOCOL` | Match IP protocol field | `ipProtocol` | +| `MATCH_IP_SOURCE_PORT_RANGE` | Match a source IP port range | `start`,`end` | +| `MATCH_IP_DEST_PORT_RANGE` | Match a destination IP port range | `start`,`end` | +| `MATCH_CHARACTERISTICS` | Match on characteristics flags | `mask`,`value` | +| `MATCH_FRAME_SIZE_RANGE` | Match a range of Ethernet frame sizes | `start`,`end` | +| `MATCH_TAGS_SAMENESS` | Match if both sides' tags differ by no more than value | `id`,`value` | +| `MATCH_TAGS_BITWISE_AND` | Match if both sides' tags AND to value | `id`,`value` | +| `MATCH_TAGS_BITWISE_OR` | Match if both sides' tags OR to value | `id`,`value` | +| `MATCH_TAGS_BITWISE_XOR` | Match if both sides' tags XOR to value | `id`,`value` | + +Important notes about rules engine behavior: + + * IPv4 and IPv6 IP address rules do not match for frames that are not IPv4 or IPv6 respectively. + * `ACTION_DEBUG_LOG` is a no-op on nodes not built with `ZT_RULES_ENGINE_DEBUGGING` enabled (see Network.cpp). If that is enabled nodes will dump a trace of rule evaluation results to *stdout* when this action is encountered but will otherwise keep evaluating rules. This is used for basic "smoke testing" of the rules engine. + * Multicast packets and packets destined for bridged devices treated a little differently. They are matched more than once. They are matched at the point of send with a NULL ZeroTier destination address, meaning that `MATCH_DEST_ZEROTIER_ADDRESS` is useless. That's because the true VL1 destination is not yet known. Then they are matched again for each true VL1 destination. On these later subsequent matches TEE actions are ignored and REDIRECT rules are interpreted as DROPs. This prevents multiple TEE or REDIRECT packets from being sent to third party devices. + * Rules in capabilities are always matched as if the current device is the sender (inbound == false). A capability specifies sender side rules that can be enforced on both sides. + +#### `/controller/network//member` + + * Purpose: Get a set of all members on this network + * Methods: GET + * Returns: { object } + +This returns a JSON object containing all member IDs as keys and their `memberRevisionCounter` values as values. + +#### `/controller/network//member/
` + + * Purpose: Create, authorize, or remove a network member + * Methods: GET, POST, DELETE + * Returns: { object } + +| Field | Type | Description | Writable | +| --------------------- | ------------- | ------------------------------------------------- | -------- | +| id | string | Member's 10-digit ZeroTier address | no | +| address | string | Member's 10-digit ZeroTier address | no | +| nwid | string | 16-digit network ID | no | +| authorized | boolean | Is member authorized? (for private networks) | YES | +| activeBridge | boolean | Member is able to bridge to other Ethernet nets | YES | +| identity | string | Member's public ZeroTier identity (if known) | no | +| ipAssignments | array[string] | Managed IP address assignments | YES | +| revision | integer | Member revision counter | no | +| vMajor | integer | Most recently known major version | no | +| vMinor | integer | Most recently known minor version | no | +| vRev | integer | Most recently known revision | no | +| vProto | integer | Most recently known protocl version | no | + +Note that managed IP assignments are only used if they fall within a managed route. Otherwise they are ignored. -| Metric Name | Type | Description | -| --- | --- | --- | -| controller_network_count | Gauge | number of networks the controller is serving | -| controller_member_count | Gauge | number of network members the controller is serving | -| controller_network_change_count | Counter | number of times a network configuration is changed | -| controller_member_change_count | Counter | number of times a network member configuration is changed | -| controller_member_auth_count | Counter | number of network member auths | -| controller_member_deauth_count | Counter | number of network member deauths| diff --git a/controller/RabbitMQ.cpp b/controller/RabbitMQ.cpp new file mode 100644 index 000000000..eec9745dc --- /dev/null +++ b/controller/RabbitMQ.cpp @@ -0,0 +1,107 @@ +#include "RabbitMQ.hpp" + +#ifdef ZT_CONTROLLER_USE_LIBPQ + +#include +#include +#include +#include + +namespace ZeroTier +{ + +RabbitMQ::RabbitMQ(MQConfig *cfg, const char *queueName) + : _mqc(cfg) + , _qName(queueName) + , _socket(NULL) + , _status(0) +{ +} + +RabbitMQ::~RabbitMQ() +{ + amqp_channel_close(_conn, _channel, AMQP_REPLY_SUCCESS); + amqp_connection_close(_conn, AMQP_REPLY_SUCCESS); + amqp_destroy_connection(_conn); +} + +void RabbitMQ::init() +{ + struct timeval tval; + memset(&tval, 0, sizeof(struct timeval)); + tval.tv_sec = 5; + + fprintf(stderr, "Initializing RabbitMQ %s\n", _qName); + _conn = amqp_new_connection(); + _socket = amqp_tcp_socket_new(_conn); + if (!_socket) { + throw std::runtime_error("Can't create socket for RabbitMQ"); + } + + _status = amqp_socket_open_noblock(_socket, _mqc->host, _mqc->port, &tval); + if (_status) { + throw std::runtime_error("Can't connect to RabbitMQ"); + } + + amqp_rpc_reply_t r = amqp_login(_conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, + _mqc->username, _mqc->password); + if (r.reply_type != AMQP_RESPONSE_NORMAL) { + throw std::runtime_error("RabbitMQ Login Error"); + } + + static int chan = 0; + { + Mutex::Lock l(_chan_m); + _channel = ++chan; + } + amqp_channel_open(_conn, _channel); + r = amqp_get_rpc_reply(_conn); + if(r.reply_type != AMQP_RESPONSE_NORMAL) { + throw std::runtime_error("Error opening communication channel"); + } + + _q = amqp_queue_declare(_conn, _channel, amqp_cstring_bytes(_qName), 0, 0, 0, 0, amqp_empty_table); + r = amqp_get_rpc_reply(_conn); + if (r.reply_type != AMQP_RESPONSE_NORMAL) { + throw std::runtime_error("Error declaring queue " + std::string(_qName)); + } + + amqp_basic_consume(_conn, _channel, amqp_cstring_bytes(_qName), amqp_empty_bytes, 0, 1, 0, amqp_empty_table); + r = amqp_get_rpc_reply(_conn); + if (r.reply_type != AMQP_RESPONSE_NORMAL) { + throw std::runtime_error("Error consuming queue " + std::string(_qName)); + } + fprintf(stderr, "RabbitMQ Init OK %s\n", _qName); +} + +std::string RabbitMQ::consume() +{ + amqp_rpc_reply_t res; + amqp_envelope_t envelope; + amqp_maybe_release_buffers(_conn); + + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + res = amqp_consume_message(_conn, &envelope, &timeout, 0); + if (res.reply_type != AMQP_RESPONSE_NORMAL) { + if (res.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION && res.library_error == AMQP_STATUS_TIMEOUT) { + // timeout waiting for message. Return empty string + return ""; + } else { + throw std::runtime_error("Error getting message"); + } + } + + std::string msg( + (const char*)envelope.message.body.bytes, + envelope.message.body.len + ); + amqp_destroy_envelope(&envelope); + return msg; +} + +} + +#endif // ZT_CONTROLLER_USE_LIBPQ diff --git a/controller/RabbitMQ.hpp b/controller/RabbitMQ.hpp new file mode 100644 index 000000000..d341681be --- /dev/null +++ b/controller/RabbitMQ.hpp @@ -0,0 +1,79 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ +#ifndef ZT_CONTROLLER_RABBITMQ_HPP +#define ZT_CONTROLLER_RABBITMQ_HPP + +namespace ZeroTier +{ +struct MQConfig { + const char *host; + int port; + const char *username; + const char *password; +}; +} + +#ifdef ZT_CONTROLLER_USE_LIBPQ + +#include "../node/Mutex.hpp" + +#include +#include +#include + +namespace ZeroTier +{ + +class RabbitMQ { +public: + RabbitMQ(MQConfig *cfg, const char *queueName); + ~RabbitMQ(); + + void init(); + + std::string consume(); + +private: + MQConfig *_mqc; + const char *_qName; + + amqp_socket_t *_socket; + amqp_connection_state_t _conn; + amqp_queue_declare_ok_t *_q; + int _status; + + int _channel; + + Mutex _chan_m; + +}; + +} + +#endif // ZT_CONTROLLER_USE_LIBPQ + +#endif // ZT_CONTROLLER_RABBITMQ_HPP + diff --git a/controller/Redis.hpp b/controller/Redis.hpp deleted file mode 100644 index c6845d517..000000000 --- a/controller/Redis.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef ZT_CONTROLLER_REDIS_HPP -#define ZT_CONTROLLER_REDIS_HPP - -#include - -namespace ZeroTier { -struct RedisConfig { - std::string hostname; - int port; - std::string password; - bool clusterMode; -}; -} // namespace ZeroTier - -#endif \ No newline at end of file diff --git a/debian/changelog b/debian/changelog index fdc85f860..d290ba949 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,235 +1,3 @@ -zerotier-one (1.14.2) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Wed, 23 Oct 2024 01:00:00 -0700 - -zerotier-one (1.14.1) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Wed, 11 Sep 2024 01:00:00 -0700 - -zerotier-one (1.14.0) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Tue, 19 Mar 2024 01:00:00 -0700 - -zerotier-one (1.12.2) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Tue, 12 Sep 2023 01:00:00 -0700 - -zerotier-one (1.12.1) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Fri, 25 Aug 2023 01:00:00 -0700 - -zerotier-one (1.12.0) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Thu, 17 Aug 2023 01:00:00 -0700 - -zerotier-one (1.10.6) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Tue, 21 Mar 2023 01:00:00 -0700 - -zerotier-one (1.10.5) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Fri, 10 Mar 2023 01:00:00 -0700 - -zerotier-one (1.10.4) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Mon, 06 Mar 2023 01:00:00 -0700 - -zerotier-one (1.10.3) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Sat, 21 Jan 2023 01:00:00 -0700 - -zerotier-one (1.10.2) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Thu, 13 Oct 2022 01:00:00 -0700 - -zerotier-one (1.10.1) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Mon, 27 Jun 2022 01:00:00 -0700 - -zerotier-one (1.10.0) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Fri, 03 Jun 2022 01:00:00 -0700 - -zerotier-one (1.8.10) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Tue, 10 May 2022 01:00:00 -0700 - -zerotier-one (1.8.9) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Mon, 25 Apr 2022 01:00:00 -0700 - -zerotier-one (1.8.8) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Mon, 11 Apr 2022 01:00:00 -0700 - -zerotier-one (1.8.7) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Mon, 21 Mar 2022 01:00:00 -0700 - -zerotier-one (1.8.6) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Mon, 07 Mar 2022 01:00:00 -0700 - -zerotier-one (1.8.5) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Fri, 17 Dec 2021 01:00:00 -0700 - -zerotier-one (1.8.4) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Mon, 23 Nov 2021 01:00:00 -0700 - -zerotier-one (1.8.3) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Mon, 15 Nov 2021 01:00:00 -0700 - -zerotier-one (1.8.2) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Mon, 08 Nov 2021 01:00:00 -0700 - -zerotier-one (1.8.1) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Wed, 20 Oct 2021 01:00:00 -0700 - -zerotier-one (1.8.0) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Wed, 15 Sep 2021 01:00:00 -0700 - -zerotier-one (1.6.6) unstable; urgency=medium - - * Backport endpoint mitigation against address collision attack. - - -- Adam Ierymenko Tue, 21 Sep 2021 01:00:00 -0700 - -zerotier-one (1.6.5) unstable; urgency=medium - - * Fix path filtering bug that could cause "software laser" effect. - * Fix printf overflow in CLI (not exploitable or security related) - * Fix Windows device enumeration issue. - - -- Adam Ierymenko Tue, 13 Apr 2021 01:00:00 -0700 - -zerotier-one (1.6.4) unstable; urgency=medium - - * REALLY fix a problem causing nodes to go into a "coma" with some network configurations. - - -- Adam Ierymenko Tue, 15 Feb 2021 01:00:00 -0700 - -zerotier-one (1.6.3-1) unstable; urgency=medium - - * Fix a problem causing nodes to go into a "coma" with some network configurations. - - -- Adam Ierymenko Tue, 02 Feb 2021 01:00:00 -0700 - -zerotier-one (1.6.2-2) unstable; urgency=medium - - * This is a minor update to the 1.6.2 package to address issues with - running on ARMv6 CPUs like the Raspberry Pi Zero and original v1 Pi. - - -- Adam Ierymenko Tue, 31 Nov 2020 01:00:00 -0700 - -zerotier-one (1.6.2) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Mon, 30 Nov 2020 01:00:00 -0700 - -zerotier-one (1.6.1) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Tue, 24 Nov 2020 01:00:00 -0700 - -zerotier-one (1.6.0) unstable; urgency=medium - - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Thu, 19 Nov 2020 01:00:00 -0700 - -zerotier-one (1.5.0) unstable; urgency=medium - - * Version 1.5.0 is actually 1.6.0-beta1 - * See RELEASE-NOTES.md for release notes. - - -- Adam Ierymenko Mon, 05 Aug 2020 01:00:00 -0700 - -zerotier-one (1.4.6) unstable; urgency=medium - - * Update default root server list - * Fix build flags on "armhf" (32-bit ARM) platforms for better - compatibility with Pi Zero and other devices. - * Fix license text in one.cpp. - * Add a clarification to LICENSE.txt. - - -- Adam Ierymenko Fri, 30 Aug 2019 01:00:00 -0700 - -zerotier-one (1.4.4) unstable; urgency=medium - - * See https://github.com/zerotier/ZeroTierOne for release notes. - * License changed to BSL 1.1 - - -- Adam Ierymenko Fri, 23 Aug 2019 01:00:00 -0700 - -zerotier-one (1.4.2-2) unstable; urgency=medium - - * See https://github.com/zerotier/ZeroTierOne for release notes. - * This is a new build that fixes a binary build issue with containers and SELinux - - -- Adam Ierymenko Thu, 04 Aug 2019 01:00:00 -0700 - -zerotier-one (1.4.2) unstable; urgency=medium - - * See https://github.com/zerotier/ZeroTierOne for release notes. - - -- Adam Ierymenko Thu, 04 Aug 2019 01:00:00 -0700 - zerotier-one (1.4.0) unstable; urgency=medium * See https://github.com/zerotier/ZeroTierOne for release notes. diff --git a/debian/compat b/debian/compat index 9a037142a..301160a93 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -10 \ No newline at end of file +8 \ No newline at end of file diff --git a/debian/control b/debian/control index b0c178ab1..1eb147e09 100644 --- a/debian/control +++ b/debian/control @@ -3,14 +3,14 @@ Maintainer: Adam Ierymenko Section: net Priority: optional Standards-Version: 3.9.6 -Build-Depends: debhelper +Build-Depends: debhelper (>= 9) Vcs-Git: git://github.com/zerotier/ZeroTierOne Vcs-Browser: https://github.com/zerotier/ZeroTierOne Homepage: https://www.zerotier.com/ Package: zerotier-one Architecture: any -Depends: adduser, libstdc++6 (>= 5), openssl +Depends: ${shlibs:Depends}, ${misc:Depends}, iproute2, adduser, libstdc++6 Homepage: https://www.zerotier.com/ Description: ZeroTier network virtualization service ZeroTier One lets you join ZeroTier virtual networks and diff --git a/debian/control.wheezy b/debian/control.wheezy index fbbc40d93..a8e240847 100644 --- a/debian/control.wheezy +++ b/debian/control.wheezy @@ -10,7 +10,7 @@ Homepage: https://www.zerotier.com/ Package: zerotier-one Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, libstdc++6 +Depends: ${shlibs:Depends}, ${misc:Depends}, iproute, libstdc++6 Homepage: https://www.zerotier.com/ Description: ZeroTier network virtualization service ZeroTier One lets you join ZeroTier virtual networks and diff --git a/debian/copyright b/debian/copyright index da3678d52..cd728a0d4 100644 --- a/debian/copyright +++ b/debian/copyright @@ -4,15 +4,21 @@ Source: https://github.com/zerotier/ZeroTierOne Files: * Copyright: 2011-2016 ZeroTier, Inc. -License: ZeroTier BSL 1.1 +License: GPL-3.0+ -License: ZeroTier BSL 1.1 - Copyright (c)2019 ZeroTier, Inc. - - Use of this software is governed by the Business Source License included - in the LICENSE.TXT file in the project's root directory. - - Change Date: 2026-01-01 - - On the date above, in accordance with the Business Source License, use - of this software will be governed by version 2.0 of the Apache License. +License: GPL-3.0+ + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". diff --git a/debian/postinst b/debian/postinst index b6e7471c4..2e673aef9 100644 --- a/debian/postinst +++ b/debian/postinst @@ -3,7 +3,7 @@ case "$1" in configure) if ! id zerotier-one >>/dev/null 2>&1; then - useradd --system --user-group --home-dir /var/lib/zerotier-one --shell /usr/sbin/nologin --no-create-home zerotier-one + useradd --system --user-group --home-dir /var/lib/zerotier-one --no-create-home zerotier-one fi ;; esac diff --git a/debian/rules b/debian/rules old mode 100644 new mode 100755 index 81fde680b..e6644d8ef --- a/debian/rules +++ b/debian/rules @@ -7,7 +7,7 @@ CXXFLAGS=-O3 -fstack-protector-strong dh $@ --with systemd override_dh_auto_build: - make + make -j 4 override_dh_systemd_start: dh_systemd_start --restart-after-upgrade diff --git a/debian/rules.wheezy b/debian/rules.wheezy old mode 100644 new mode 100755 diff --git a/debian/zerotier-one.service b/debian/zerotier-one.service index 9d6a21b0a..133d4490c 100644 --- a/debian/zerotier-one.service +++ b/debian/zerotier-one.service @@ -1,6 +1,6 @@ [Unit] Description=ZeroTier One -After=network-online.target network.target +After=network-online.target Wants=network-online.target [Service] diff --git a/doc/README.md b/doc/README.md index 681954cc5..707c64a9a 100644 --- a/doc/README.md +++ b/doc/README.md @@ -3,4 +3,4 @@ Manual Pages and Other Documentation Use "./build.sh" to build the manual pages. -You'll need either Node.js/npm installed (script will then automatically install the npm *marked-man* package) or */usr/bin/ronn*. The latter is a Ruby program packaged on some distributions as *rubygem-ronn* or *ruby-ronn* or installable as *gem install ronn*. The Node *marked-man* package and *ronn* from RubyGems are two roughly equivalent alternatives for compiling Markdown into roff/man format. +You'll need either NodeJS/npm installed (script will then automatically install the npm *marked-man* package) or */usr/bin/ronn*. The latter is a Ruby program packaged on some distributions as *rubygem-ronn* or *ruby-ronn* or installable as *gem install ronn*. The Node *marked-man* package and *ronn* from rubygems are two roughly equivalent alternatives for compiling MarkDown into roff/man format. diff --git a/doc/zerotier-one.8.md b/doc/zerotier-one.8.md index 4f4655076..bd31d5c80 100644 --- a/doc/zerotier-one.8.md +++ b/doc/zerotier-one.8.md @@ -81,7 +81,7 @@ These are found in the service's working directory. If the ZeroTier One service is built with the network controller enabled, it periodically backs up its controller.db database in this file (currently every 5 minutes if there have been changes). Since this file is not a currently in use SQLite3 database it's safer to back up without corruption. On new backups the file is rotated out rather than being rewritten in place. * `iddb.d/` (directory): - Caches the public identity of every peer ZeroTier has spoken with in the last 60 days. This directory and its contents can be deleted, but this may result in slower connection initiations since it will require that we go out and re-fetch full identities for peers we're speaking to. + Caches the public identity of every peer ZeroTier has spoken with in the last 60 days. This directory and its contents can be deleted, but this may result in slower connection initations since it will require that we go out and re-fetch full identities for peers we're speaking to. * `networks.d` (directory): This caches network configurations and certificate information for networks you belong to. ZeroTier scans this directory for .conf files on startup to recall its networks, so "touch"ing an empty .conf file in this directory is a way of pre-configuring ZeroTier to join a specific network on startup without using the API. If the config file is empty ZeroTIer will just fetch it from the network's controller. diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 000000000..0f0750c62 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,19 @@ +# Dockerfile for ZeroTier Central Controllers +FROM centos:7 +MAINTAINER Adam Ierymekno , Grant Limberg + +RUN yum update -y +RUN yum install -y https://download.postgresql.org/pub/repos/yum/10/redhat/rhel-7-x86_64/pgdg-centos10-10-2.noarch.rpm +RUN yum install -y bash postgresql10 libpqxx-devel + +RUN yum -y install epel-release && yum -y update && yum clean all +RUN yum -y install clang jemalloc jemalloc-devel + + +ADD zerotier-one /usr/local/bin/zerotier-one +RUN chmod a+x /usr/local/bin/zerotier-one + +ADD docker/main.sh / +RUN chmod a+x /main.sh + +ENTRYPOINT /main.sh diff --git a/docker/main.sh b/docker/main.sh new file mode 100644 index 000000000..b8d3b142c --- /dev/null +++ b/docker/main.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +if [ -z "$ZT_IDENTITY_PATH" ]; then + echo '*** FAILED: ZT_IDENTITY_PATH environment variable is not defined' + exit 1 +fi +if [ -z "$ZT_DB_HOST" ]; then + echo '*** FAILED: ZT_DB_HOST environment variable not defined' + exit 1 +fi +if [ -z "$ZT_DB_PORT" ]; then + echo '*** FAILED: ZT_DB_PORT environment variable not defined' + exit 1 +fi +if [ -z "$ZT_DB_NAME" ]; then + echo '*** FAILED: ZT_DB_NAME environment variable not defined' + exit 1 +fi +if [ -z "$ZT_DB_USER" ]; then + echo '*** FAILED: ZT_DB_USER environment variable not defined' + exit 1 +fi +if [ -z "$ZT_DB_PASSWORD" ]; then + echo '*** FAILED: ZT_DB_PASSWORD environment variable not defined' + exit 1 +fi + +RMQ="" +if [ "$ZT_USE_RABBITMQ" == "true" ]; then + if [ -z "$RABBITMQ_HOST" ]; then + echo '*** FAILED: RABBITMQ_HOST environment variable not defined' + exit 1 + fi + if [ -z "$RABBITMQ_PORT" ]; then + echo '*** FAILED: RABBITMQ_PORT environment variable not defined' + exit 1 + fi + if [ -z "$RABBITMQ_USERNAME" ]; then + echo '*** FAILED: RABBITMQ_USERNAME environment variable not defined' + exit 1 + fi + if [ -z "$RABBITMQ_PASSWORD" ]; then + echo '*** FAILED: RABBITMQ_PASSWORD environment variable not defined' + exit 1 + fi + RMQ=", \"rabbitmq\": { + \"host\": \"${RABBITMQ_HOST}\", + \"port\": ${RABBITMQ_PORT}, + \"username\": \"${RABBITMQ_USERNAME}\", + \"password\": \"${RABBITMQ_PASSWORD}\" + }" +fi + +mkdir -p /var/lib/zerotier-one + +pushd /var/lib/zerotier-one +ln -s $ZT_IDENTITY_PATH/identity.public identity.public +ln -s $ZT_IDENTITY_PATH/identity.secret identity.secret +popd + +DEFAULT_PORT=9993 + +echo "{ + \"settings\": { + \"portMappingEnabled\": true, + \"softwareUpdate\": \"disable\", + \"interfacePrefixBlacklist\": [ + \"inot\", + \"nat64\" + ], + \"controllerDbPath\": \"postgres:host=${ZT_DB_HOST} port=${ZT_DB_PORT} dbname=${ZT_DB_NAME} user=${ZT_DB_USER} password=${ZT_DB_PASSWORD} sslmode=prefer sslcert=${DB_CLIENT_CERT} sslkey=${DB_CLIENT_KEY} sslrootcert=${DB_SERVER_CA}\" + ${RMQ} + } +} +" > /var/lib/zerotier-one/local.conf + +export GLIBCXX_FORCE_NEW=1 +export GLIBCPP_FORCE_NEW=1 +export LD_PRELOAD="/usr/lib64/libjemalloc.so" +exec /usr/local/bin/zerotier-one -p${ZT_CONTROLLER_PORT:-$DEFAULT_PORT} /var/lib/zerotier-one diff --git a/entrypoint.sh.release b/entrypoint.sh.release deleted file mode 100644 index da9b8f8c8..000000000 --- a/entrypoint.sh.release +++ /dev/null @@ -1,130 +0,0 @@ -#!/bin/sh - -grepzt() { - [ -f /var/lib/zerotier-one/zerotier-one.pid -a -n "$(cat /var/lib/zerotier-one/zerotier-one.pid 2>/dev/null)" -a -d "/proc/$(cat /var/lib/zerotier-one/zerotier-one.pid 2>/dev/null)" ] - return $? -} - -mkztfile() { - file=$1 - mode=$2 - content=$3 - echo "creating $file" - mkdir -p /var/lib/zerotier-one - echo -n "$content" > "/var/lib/zerotier-one/$file" - chmod "$mode" "/var/lib/zerotier-one/$file" -} - -if [ "x$ZEROTIER_API_SECRET" != "x" ] -then - mkztfile authtoken.secret 0600 "$ZEROTIER_API_SECRET" - mkztfile metricstoken.secret 0600 "$ZEROTIER_API_SECRET" -fi - -if [ "x$ZEROTIER_IDENTITY_PUBLIC" != "x" ] -then - mkztfile identity.public 0644 "$ZEROTIER_IDENTITY_PUBLIC" -fi - -if [ "x$ZEROTIER_IDENTITY_SECRET" != "x" ] -then - mkztfile identity.secret 0600 "$ZEROTIER_IDENTITY_SECRET" -fi - -if [ "x$ZEROTIER_LOCAL_CONF" != "x" ] -then - mkztfile local.conf 0644 "$ZEROTIER_LOCAL_CONF" -fi - -mkztfile zerotier-one.port 0600 "9993" - -killzerotier() { - log "Killing zerotier" - kill $(cat /var/lib/zerotier-one/zerotier-one.pid 2>/dev/null) - exit 0 -} - -log_header() { - echo -n "\r=>" -} - -log_detail_header() { - echo -n "\r===>" -} - -log() { - echo "$(log_header)" "$@" -} - -log_params() { - title=$1 - shift - log "$title" "[$@]" -} - -log_detail() { - echo "$(log_detail_header)" "$@" -} - -log_detail_params() { - title=$1 - shift - log_detail "$title" "[$@]" -} - -trap killzerotier INT TERM - -log "Configuring networks to join" -mkdir -p /var/lib/zerotier-one/networks.d - -log_params "Joining networks from command line:" $@ -for i in "$@" -do - log_detail_params "Configuring join:" "$i" - touch "/var/lib/zerotier-one/networks.d/${i}.conf" -done - -if [ "x$ZEROTIER_JOIN_NETWORKS" != "x" ] -then - log_params "Joining networks from environment:" $ZEROTIER_JOIN_NETWORKS - for i in $ZEROTIER_JOIN_NETWORKS - do - log_detail_params "Configuring join:" "$i" - touch "/var/lib/zerotier-one/networks.d/${i}.conf" - done -fi - -log "Starting ZeroTier" -nohup /usr/sbin/zerotier-one & - -while ! grepzt -do - log_detail "ZeroTier hasn't started, waiting a second" - - if [ -f nohup.out ] - then - tail -n 10 nohup.out - fi - - sleep 1 -done - -log_params "Writing healthcheck for networks:" $@ - -cat >/healthcheck.sh < /etc/ld.so.conf.d/usr-local-lib64.conf && ldconfig - -ADD ext/central-controller-docker/main.sh / -RUN chmod a+x /main.sh - -ENTRYPOINT /main.sh diff --git a/ext/central-controller-docker/Dockerfile.builder b/ext/central-controller-docker/Dockerfile.builder deleted file mode 100644 index e96a61995..000000000 --- a/ext/central-controller-docker/Dockerfile.builder +++ /dev/null @@ -1,23 +0,0 @@ -# Dockerfile for building ZeroTier Central Controllers -FROM debian:bookworm - -RUN apt update && apt upgrade -y -RUN apt -y install \ - build-essential \ - pkg-config \ - bash \ - clang \ - libjemalloc2 \ - libjemalloc-dev \ - libpq5 \ - libpq-dev \ - openssl \ - libssl-dev \ - postgresql-client \ - postgresql-client-common \ - curl \ - google-perftools \ - libgoogle-perftools-dev \ - protobuf-compiler - -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y diff --git a/ext/central-controller-docker/Dockerfile.run_base b/ext/central-controller-docker/Dockerfile.run_base deleted file mode 100644 index 606ebfcfb..000000000 --- a/ext/central-controller-docker/Dockerfile.run_base +++ /dev/null @@ -1,17 +0,0 @@ -FROM debian:bookworm - - - -RUN apt update && apt upgrade -y -RUN apt -y install \ - netcat-traditional \ - postgresql-client \ - postgresql-client-common \ - libjemalloc2 \ - libpq5 \ - curl \ - binutils \ - perf-tools-unstable \ - google-perftools \ - gnupg - diff --git a/ext/central-controller-docker/Makefile b/ext/central-controller-docker/Makefile deleted file mode 100644 index 46ec8ee16..000000000 --- a/ext/central-controller-docker/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -registry = registry.zerotier.com/zerotier - -all: controller-builder controller-runbase - -buildx: - @echo "docker buildx create" - # docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - docker run --privileged --rm tonistiigi/binfmt --install all - @echo docker buildx create --name multiarch --driver docker-container --use - @echo docker buildx inspect --bootstrap - -controller-builder: buildx - docker buildx build --no-cache --platform linux/amd64,linux/arm64 -t $(registry)/ctlbuild:latest -f Dockerfile.builder . --push - -controller-runbase: buildx - docker buildx build --no-cache --platform linux/amd64,linux/arm64 -t $(registry)/ctlrun:latest -f Dockerfile.run_base . --push diff --git a/ext/central-controller-docker/README.md b/ext/central-controller-docker/README.md deleted file mode 100644 index a954b1c37..000000000 --- a/ext/central-controller-docker/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# ZeroTier Central Controller Docker Image - -Dockerfile & startup script for use with [ZeroTier Central](https://my.zerotier.com). Not intended for public use. diff --git a/ext/central-controller-docker/main.sh b/ext/central-controller-docker/main.sh deleted file mode 100755 index d99df03ad..000000000 --- a/ext/central-controller-docker/main.sh +++ /dev/null @@ -1,127 +0,0 @@ -#!/bin/bash - -if [ -z "$ZT_DB_HOST" ]; then - echo '*** FAILED: ZT_DB_HOST environment variable not defined' - exit 1 -fi -if [ -z "$ZT_DB_PORT" ]; then - echo '*** FAILED: ZT_DB_PORT environment variable not defined' - exit 1 -fi -if [ -z "$ZT_DB_NAME" ]; then - echo '*** FAILED: ZT_DB_NAME environment variable not defined' - exit 1 -fi -if [ -z "$ZT_DB_USER" ]; then - echo '*** FAILED: ZT_DB_USER environment variable not defined' - exit 1 -fi -if [ -z "$ZT_DB_PASSWORD" ]; then - echo '*** FAILED: ZT_DB_PASSWORD environment variable not defined' - exit 1 -fi -if [ -z "$ZT_DB_TYPE" ]; then - ZT_DB_TYPE="postgres" -fi - -REDIS="" -if [ "$ZT_USE_REDIS" == "true" ]; then - if [ -z "$ZT_REDIS_HOST" ]; then - echo '*** FAILED: ZT_REDIS_HOST environment variable not defined' - exit 1 - fi - - if [ -z "$ZT_REDIS_PORT" ]; then - echo '*** FAILED: ZT_REDIS_PORT enivronment variable not defined' - exit 1 - fi - - if [ -z "$ZT_REDIS_CLUSTER_MODE" ]; then - echo '*** FAILED: ZT_REDIS_CLUSTER_MODE environment variable not defined' - exit 1 - fi - - REDIS="\"redis\": { - \"hostname\": \"${ZT_REDIS_HOST}\", - \"port\": ${ZT_REDIS_PORT}, - \"clusterMode\": ${ZT_REDIS_CLUSTER_MODE}, - \"password\": \"${ZT_REDIS_PASSWORD}\" - } - " -else - REDIS="\"redis\": null" -fi - -mkdir -p /var/lib/zerotier-one - -pushd /var/lib/zerotier-one -if [ -d "$ZT_IDENTITY_PATH" ]; then - echo '*** Using existing ZT identity from path $ZT_IDENTITY_PATH' - - ln -s $ZT_IDENTITY_PATH/identity.public identity.public - ln -s $ZT_IDENTITY_PATH/identity.secret identity.secret - if [ -f "$ZT_IDENTITY_PATH/authtoken.secret" ]; then - ln -s $ZT_IDENTITY_PATH/authtoken.secret authtoken.secret - fi -fi -popd - -DEFAULT_PORT=9993 -DEFAULT_LB_MODE=false - -APP_NAME="controller-$(cat /var/lib/zerotier-one/identity.public | cut -d ':' -f 1)" - -echo "{ - \"settings\": { - \"controllerDbPath\": \"${ZT_DB_TYPE}:host=${ZT_DB_HOST} port=${ZT_DB_PORT} dbname=${ZT_DB_NAME} user=${ZT_DB_USER} password=${ZT_DB_PASSWORD} application_name=${APP_NAME} sslmode=prefer sslcert=${DB_CLIENT_CERT} sslkey=${DB_CLIENT_KEY} sslrootcert=${DB_SERVER_CA}\", - \"portMappingEnabled\": true, - \"softwareUpdate\": \"disable\", - \"interfacePrefixBlacklist\": [ - \"inot\", - \"nat64\" - ], - \"lowBandwidthMode\": ${ZT_LB_MODE:-$DEFAULT_LB_MODE}, - \"ssoRedirectURL\": \"${ZT_SSO_REDIRECT_URL}\", - \"allowManagementFrom\": [\"127.0.0.1\", \"::1\", \"10.0.0.0/8\"], - ${REDIS} - } -} -" > /var/lib/zerotier-one/local.conf - -if [ -n "$DB_SERVER_CA" ]; then - echo "secret list" - chmod 600 /secrets/db/*.pem - ls -l /secrets/db/ - until /usr/bin/pg_isready -h ${ZT_DB_HOST} -p ${ZT_DB_PORT} -d "sslmode=prefer sslcert=${DB_CLIENT_CERT} sslkey=${DB_CLIENT_KEY} sslrootcert=${DB_SERVER_CA}"; do - echo "Waiting for PostgreSQL..."; - sleep 2; - done -else - until /usr/bin/pg_isready -h ${ZT_DB_HOST} -p ${ZT_DB_PORT}; do - echo "Waiting for PostgreSQL..."; - sleep 2; - done -fi - -if [ "$ZT_DB_TYPE" == "cv2" ]; then - echo "Migrating database (if needed)..." - if [ -n "$DB_SERVER_CA" ]; then - /usr/local/bin/migrate -source file:///migrations -database "postgres://$ZT_DB_USER:$ZT_DB_PASSWORD@$ZT_DB_HOST:$ZT_DB_PORT/$ZT_DB_NAME?x-migrations-table=controller_migrations&sslmode=verify-full&sslrootcert=$DB_SERVER_CA&sslcert=$DB_CLIENT_CERT&sslkey=$DB_CLIENT_KEY" up - else - /usr/local/bin/migrate -source file:///migrations -database "postgres://$ZT_DB_USER:$ZT_DB_PASSWORD@$ZT_DB_HOST:$ZT_DB_PORT/$ZT_DB_NAME?x-migrations-table=controller_migrations&sslmode=disable" up - fi -fi - -if [ -n "$ZT_TEMPORAL_HOST" ] && [ -n "$ZT_TEMPORAL_PORT" ]; then - echo "waiting for temporal..." - while ! nc -z ${ZT_TEMPORAL_HOST} ${ZT_TEMPORAL_PORT}; do - echo "waiting..."; - sleep 1; - done - echo "Temporal is up" -fi - -export GLIBCXX_FORCE_NEW=1 -export GLIBCPP_FORCE_NEW=1 -export LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libjemalloc.so.2" -exec /usr/local/bin/zerotier-one -p${ZT_CONTROLLER_PORT:-$DEFAULT_PORT} /var/lib/zerotier-one diff --git a/ext/central-controller-docker/migrations/0001_init.down.sql b/ext/central-controller-docker/migrations/0001_init.down.sql deleted file mode 100644 index 03dc63c81..000000000 --- a/ext/central-controller-docker/migrations/0001_init.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -DROP TABLE IF EXISTS network_memberships_ctl; -DROP TABLE IF EXISTS networks_ctl; -DROP TABLE IF EXISTS controllers_ctl; \ No newline at end of file diff --git a/ext/central-controller-docker/migrations/0001_init.up.sql b/ext/central-controller-docker/migrations/0001_init.up.sql deleted file mode 100644 index 90d29e889..000000000 --- a/ext/central-controller-docker/migrations/0001_init.up.sql +++ /dev/null @@ -1,47 +0,0 @@ --- inits controller db schema - -CREATE TABLE IF NOT EXISTS controllers_ctl ( - id text NOT NULL PRIMARY KEY, - hostname text, - last_heartbeat timestamp with time zone, - public_identity text NOT NULL, - version text -); - -CREATE TABLE IF NOT EXISTS networks_ctl ( - id character varying(22) NOT NULL PRIMARY KEY, - name text NOT NULL, - configuration jsonb DEFAULT '{}'::jsonb NOT NULL, - controller_id text REFERENCES controllers_ctl(id), - revision integer DEFAULT 0 NOT NULL, - last_modified timestamp with time zone DEFAULT now(), - creation_time timestamp with time zone DEFAULT now() -); - -CREATE TABLE IF NOT EXISTS network_memberships_ctl ( - device_id character varying(22) NOT NULL, - network_id character varying(22) NOT NULL REFERENCES networks_ctl(id), - authorized boolean, - active_bridge boolean, - ip_assignments text[], - no_auto_assign_ips boolean, - sso_exempt boolean, - authentication_expiry_time timestamp with time zone, - capabilities jsonb, - creation_time timestamp with time zone DEFAULT now(), - last_modified timestamp with time zone DEFAULT now(), - identity text DEFAULT ''::text, - last_authorized_credential text, - last_authorized_time timestamp with time zone, - last_deauthorized_time timestamp with time zone, - last_seen jsonb DEFAULT '{}'::jsonb NOT NULL, -- in the context of the network - remote_trace_level integer DEFAULT 0 NOT NULL, - remote_trace_target text DEFAULT ''::text NOT NULL, - revision integer DEFAULT 0 NOT NULL, - tags jsonb, - version_major integer DEFAULT 0 NOT NULL, - version_minor integer DEFAULT 0 NOT NULL, - version_revision integer DEFAULT 0 NOT NULL, - version_protocol integer DEFAULT 0 NOT NULL, - PRIMARY KEY (device_id, network_id) -); diff --git a/ext/central-controller-docker/migrations/0002_os_arch.down.sql b/ext/central-controller-docker/migrations/0002_os_arch.down.sql deleted file mode 100644 index 3c90f0a2e..000000000 --- a/ext/central-controller-docker/migrations/0002_os_arch.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE network_memberships_ctl - DROP COLUMN os, - DROP COLUMN arch; \ No newline at end of file diff --git a/ext/central-controller-docker/migrations/0002_os_arch.up.sql b/ext/central-controller-docker/migrations/0002_os_arch.up.sql deleted file mode 100644 index 095f7c698..000000000 --- a/ext/central-controller-docker/migrations/0002_os_arch.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE network_memberships_ctl - ADD COLUMN os TEXT NOT NULL DEFAULT 'unknown', - ADD COLUMN arch TEXT NOT NULL DEFAULT 'unknown'; \ No newline at end of file diff --git a/ext/inja/LICENSE b/ext/cpp-httplib/LICENSE similarity index 95% rename from ext/inja/LICENSE rename to ext/cpp-httplib/LICENSE index 9e06bea07..3e5ed359a 100644 --- a/ext/inja/LICENSE +++ b/ext/cpp-httplib/LICENSE @@ -1,6 +1,6 @@ -MIT License +The MIT License (MIT) -Copyright (c) 2018-2021 Berscheid +Copyright (c) 2017 yhirose Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,3 +19,4 @@ 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/cpp-httplib/README.md b/ext/cpp-httplib/README.md new file mode 100644 index 000000000..2bd234540 --- /dev/null +++ b/ext/cpp-httplib/README.md @@ -0,0 +1,259 @@ +cpp-httplib +=========== + +[![Build Status](https://travis-ci.org/yhirose/cpp-httplib.svg?branch=master)](https://travis-ci.org/yhirose/cpp-httplib) +[![Bulid Status](https://ci.appveyor.com/api/projects/status/github/yhirose/cpp-httplib?branch=master&svg=true)](https://ci.appveyor.com/project/yhirose/cpp-httplib) + +A C++ header-only cross platform HTTP/HTTPS library. + +It's extremely easy to setup. Just include **httplib.h** file in your code! + +Inspired by [Sinatra](http://www.sinatrarb.com/) and [express](https://github.com/visionmedia/express). + +Server Example +-------------- + +```c++ +#include + +int main(void) +{ + using namespace httplib; + + Server svr; + + svr.Get("/hi", [](const Request& req, Response& res) { + res.set_content("Hello World!", "text/plain"); + }); + + svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) { + auto numbers = req.matches[1]; + res.set_content(numbers, "text/plain"); + }); + + svr.listen("localhost", 1234); +} +``` + +`Post`, `Put`, `Delete` and `Options` methods are also supported. + +### Bind a socket to multiple interfaces and any available port + +```cpp +int port = svr.bind_to_any_port("0.0.0.0"); +svr.listen_after_bind(); +``` + +### Method Chain + +```cpp +svr.Get("/get", [](const auto& req, auto& res) { + res.set_content("get", "text/plain"); + }) + .Post("/post", [](const auto& req, auto& res) { + res.set_content(req.body(), "text/plain"); + }) + .listen("localhost", 1234); +``` + +### Static File Server + +```cpp +svr.set_base_dir("./www"); +``` + +### Logging + +```cpp +svr.set_logger([](const auto& req, const auto& res) { + your_logger(req, res); +}); +``` + +### Error Handler + +```cpp +svr.set_error_handler([](const auto& req, auto& res) { + const char* fmt = "

Error Status: %d

"; + char buf[BUFSIZ]; + snprintf(buf, sizeof(buf), fmt, res.status); + res.set_content(buf, "text/html"); +}); +``` + +### 'multipart/form-data' POST data + +```cpp +svr.Post("/multipart", [&](const auto& req, auto& res) { + auto size = req.files.size(); + auto ret = req.has_file("name1")); + const auto& file = req.get_file_value("name1"); + // file.filename; + // file.content_type; + auto body = req.body.substr(file.offset, file.length)); +}) +``` + +Client Example +-------------- + +### GET + +```c++ +#include +#include + +int main(void) +{ + httplib::Client cli("localhost", 1234); + + auto res = cli.Get("/hi"); + if (res && res->status == 200) { + std::cout << res->body << std::endl; + } +} +``` + +### GET with Content Receiver + +```c++ + std::string body; + auto res = cli.Get("/large-data", [&](const char *data, size_t len) { + body.append(data, len); + }); + assert(res->body.empty()); +``` + +### POST + +```c++ +res = cli.Post("/post", "text", "text/plain"); +res = cli.Post("/person", "name=john1¬e=coder", "application/x-www-form-urlencoded"); +``` + +### POST with parameters + +```c++ +httplib::Params params; +params.emplace("name", "john"); +params.emplace("note", "coder"); + +auto res = cli.Post("/post", params); +``` + or + +```c++ +httplib::Params params{ + { "name", "john" }, + { "note", "coder" } +}; + +auto res = cli.Post("/post", params); +``` + +### PUT + +```c++ +res = cli.Put("/resource/foo", "text", "text/plain"); +``` + +### DELETE + +```c++ +res = cli.Delete("/resource/foo"); +``` + +### OPTIONS + +```c++ +res = cli.Options("*"); +res = cli.Options("/resource/foo"); +``` + +### Connection Timeout + +```c++ +httplib::Client cli("localhost", 8080, 5); // timeouts in 5 seconds +``` +### With Progress Callback + +```cpp +httplib::Client client(url, port); + +// prints: 0 / 000 bytes => 50% complete +std::shared_ptr res = + cli.Get("/", [](uint64_t len, uint64_t total) { + printf("%lld / %lld bytes => %d%% complete\n", + len, total, + (int)((len/total)*100)); + return true; // return 'false' if you want to cancel the request. + } +); +``` + +![progress](https://user-images.githubusercontent.com/236374/33138910-495c4ecc-cf86-11e7-8693-2fc6d09615c4.gif) + +This feature was contributed by [underscorediscovery](https://github.com/yhirose/cpp-httplib/pull/23). + +### Basic Authentication + +```cpp +httplib::Client cli("httplib.org"); + +auto res = cli.Get("/basic-auth/hello/world", { + httplib::make_basic_authentication_header("hello", "world") +}); +// res->status should be 200 +// res->body should be "{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n". +``` + +### Range + +```cpp +httplib::Client cli("httpbin.org"); + +auto res = cli.Get("/range/32", { + httplib::make_range_header(1, 10) // 'Range: bytes=1-10' +}); +// res->status should be 206. +// res->body should be "bcdefghijk". +``` + +OpenSSL Support +--------------- + +SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked. + +```c++ +#define CPPHTTPLIB_OPENSSL_SUPPORT + +SSLServer svr("./cert.pem", "./key.pem"); + +SSLClient cli("localhost", 8080); +cli.set_ca_cert_path("./ca-bundle.crt"); +cli.enable_server_certificate_verification(true); +``` + +Zlib Support +------------ + +'gzip' compression is available with `CPPHTTPLIB_ZLIB_SUPPORT`. + +The server applies gzip compression to the following MIME type contents: + + * all text types + * image/svg+xml + * application/javascript + * application/json + * application/xml + * application/xhtml+xml + +NOTE +---- + +g++ 4.8 cannot build this library since `` in g++4.8 is [broken](https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions). + +License +------- + +MIT license (© 2019 Yuji Hirose) diff --git a/ext/cpp-httplib/httplib.h b/ext/cpp-httplib/httplib.h index dfdd260ab..5adfc2afe 100644 --- a/ext/cpp-httplib/httplib.h +++ b/ext/cpp-httplib/httplib.h @@ -1,126 +1,13 @@ // // httplib.h // -// Copyright (c) 2024 Yuji Hirose. All rights reserved. +// Copyright (c) 2019 Yuji Hirose. All rights reserved. // MIT License // #ifndef CPPHTTPLIB_HTTPLIB_H #define CPPHTTPLIB_HTTPLIB_H -#define CPPHTTPLIB_VERSION "0.15.3" - -/* - * Configuration - */ - -#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND -#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 -#endif - -#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT -#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 -#endif - -#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND -#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300 -#endif - -#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND -#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0 -#endif - -#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND -#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 -#endif - -#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND -#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 -#endif - -#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND -#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5 -#endif - -#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND -#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0 -#endif - -#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND -#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0 -#endif - -#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND -#ifdef _WIN32 -#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000 -#else -#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0 -#endif -#endif - -#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH -#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 -#endif - -#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH -#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192 -#endif - -#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT -#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20 -#endif - -#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT -#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024 -#endif - -#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH -#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits::max)()) -#endif - -#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH -#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192 -#endif - -#ifndef CPPHTTPLIB_RANGE_MAX_COUNT -#define CPPHTTPLIB_RANGE_MAX_COUNT 1024 -#endif - -#ifndef CPPHTTPLIB_TCP_NODELAY -#define CPPHTTPLIB_TCP_NODELAY false -#endif - -#ifndef CPPHTTPLIB_RECV_BUFSIZ -#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u) -#endif - -#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ -#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u) -#endif - -#ifndef CPPHTTPLIB_THREAD_POOL_COUNT -#define CPPHTTPLIB_THREAD_POOL_COUNT \ - ((std::max)(8u, std::thread::hardware_concurrency() > 0 \ - ? std::thread::hardware_concurrency() - 1 \ - : 0)) -#endif - -#ifndef CPPHTTPLIB_RECV_FLAGS -#define CPPHTTPLIB_RECV_FLAGS 0 -#endif - -#ifndef CPPHTTPLIB_SEND_FLAGS -#define CPPHTTPLIB_SEND_FLAGS 0 -#endif - -#ifndef CPPHTTPLIB_LISTEN_BACKLOG -#define CPPHTTPLIB_LISTEN_BACKLOG 5 -#endif - -/* - * Headers - */ - #ifdef _WIN32 #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS @@ -130,26 +17,16 @@ #define _CRT_NONSTDC_NO_DEPRECATE #endif //_CRT_NONSTDC_NO_DEPRECATE -#if defined(_MSC_VER) -#if _MSC_VER < 1900 -#error Sorry, Visual Studio versions prior to 2015 are not supported -#endif - -#pragma comment(lib, "ws2_32.lib") - -#ifdef _WIN64 -using ssize_t = __int64; -#else -using ssize_t = long; -#endif +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf_s #endif // _MSC_VER #ifndef S_ISREG -#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG) +#define S_ISREG(m) (((m)&S_IFREG) == S_IFREG) #endif // S_ISREG #ifndef S_ISDIR -#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR) +#define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR) #endif // S_ISDIR #ifndef NOMINMAX @@ -160,689 +37,202 @@ using ssize_t = long; #include #include -#ifndef WSA_FLAG_NO_HANDLE_INHERIT -#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 -#endif +#pragma comment(lib, "ws2_32.lib") -using socket_t = SOCKET; -#ifdef CPPHTTPLIB_USE_POLL -#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) -#endif - -#else // not _WIN32 +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif // strcasecmp +typedef SOCKET socket_t; +#else #include -#if !defined(_AIX) && !defined(__MVS__) -#include -#endif -#ifdef __MVS__ -#include -#ifndef NI_MAXHOST -#define NI_MAXHOST 1025 -#endif -#endif -#include +#include #include #include -#ifdef __linux__ -#include -#endif -#include -#ifdef CPPHTTPLIB_USE_POLL -#include -#endif -#include #include -#include +#include #include #include -#include #include -using socket_t = int; -#ifndef INVALID_SOCKET +typedef int socket_t; #define INVALID_SOCKET (-1) -#endif #endif //_WIN32 -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include #include #include #include -#include -#include -#include #include #include #include -#include #include -#include -#include #include #include #include -#include -#include -#include #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -#ifdef _WIN32 -#include - -// these are defined in wincrypt.h and it breaks compilation if BoringSSL is -// used -#undef X509_NAME -#undef X509_CERT_PAIR -#undef X509_EXTENSIONS -#undef PKCS7_SIGNER_INFO - -#ifdef _MSC_VER -#pragma comment(lib, "crypt32.lib") -#endif -#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__) -#include -#if TARGET_OS_OSX -#include -#include -#endif // TARGET_OS_OSX -#endif // _WIN32 - #include -#include #include #include -#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK) -#include +#if OPENSSL_VERSION_NUMBER < 0x10100000L +inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) { + return M_ASN1_STRING_data(asn1); +} #endif - -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x30000000L -#error Sorry, OpenSSL versions prior to 3.0.0 are not supported -#endif - #endif #ifdef CPPHTTPLIB_ZLIB_SUPPORT #include #endif -#ifdef CPPHTTPLIB_BROTLI_SUPPORT -#include -#include -#endif - /* - * Declaration + * Configuration */ +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0 +#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 +#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 +#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 +#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 +#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits::max)() +#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u) + namespace httplib { namespace detail { -/* - * Backport std::make_unique from C++14. - * - * NOTE: This code came up with the following stackoverflow post: - * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique - * - */ - -template -typename std::enable_if::value, std::unique_ptr>::type -make_unique(Args &&...args) { - return std::unique_ptr(new T(std::forward(args)...)); -} - -template -typename std::enable_if::value, std::unique_ptr>::type -make_unique(std::size_t n) { - typedef typename std::remove_extent::type RT; - return std::unique_ptr(new RT[n]); -} - struct ci { bool operator()(const std::string &s1, const std::string &s2) const { - return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), - s2.end(), - [](unsigned char c1, unsigned char c2) { - return ::tolower(c1) < ::tolower(c2); - }); + return std::lexicographical_compare( + s1.begin(), s1.end(), s2.begin(), s2.end(), + [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); }); } }; -// This is based on -// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189". - -struct scope_exit { - explicit scope_exit(std::function &&f) - : exit_function(std::move(f)), execute_on_destruction{true} {} - - scope_exit(scope_exit &&rhs) noexcept - : exit_function(std::move(rhs.exit_function)), - execute_on_destruction{rhs.execute_on_destruction} { - rhs.release(); - } - - ~scope_exit() { - if (execute_on_destruction) { this->exit_function(); } - } - - void release() { this->execute_on_destruction = false; } - -private: - scope_exit(const scope_exit &) = delete; - void operator=(const scope_exit &) = delete; - scope_exit &operator=(scope_exit &&) = delete; - - std::function exit_function; - bool execute_on_destruction; -}; - } // namespace detail -enum StatusCode { - // Information responses - Continue_100 = 100, - SwitchingProtocol_101 = 101, - Processing_102 = 102, - EarlyHints_103 = 103, +enum class HttpVersion { v1_0 = 0, v1_1 }; - // Successful responses - OK_200 = 200, - Created_201 = 201, - Accepted_202 = 202, - NonAuthoritativeInformation_203 = 203, - NoContent_204 = 204, - ResetContent_205 = 205, - PartialContent_206 = 206, - MultiStatus_207 = 207, - AlreadyReported_208 = 208, - IMUsed_226 = 226, +typedef std::multimap Headers; - // Redirection messages - MultipleChoices_300 = 300, - MovedPermanently_301 = 301, - Found_302 = 302, - SeeOther_303 = 303, - NotModified_304 = 304, - UseProxy_305 = 305, - unused_306 = 306, - TemporaryRedirect_307 = 307, - PermanentRedirect_308 = 308, +template +std::pair make_range_header(uint64_t value, + Args... args); - // Client error responses - BadRequest_400 = 400, - Unauthorized_401 = 401, - PaymentRequired_402 = 402, - Forbidden_403 = 403, - NotFound_404 = 404, - MethodNotAllowed_405 = 405, - NotAcceptable_406 = 406, - ProxyAuthenticationRequired_407 = 407, - RequestTimeout_408 = 408, - Conflict_409 = 409, - Gone_410 = 410, - LengthRequired_411 = 411, - PreconditionFailed_412 = 412, - PayloadTooLarge_413 = 413, - UriTooLong_414 = 414, - UnsupportedMediaType_415 = 415, - RangeNotSatisfiable_416 = 416, - ExpectationFailed_417 = 417, - ImATeapot_418 = 418, - MisdirectedRequest_421 = 421, - UnprocessableContent_422 = 422, - Locked_423 = 423, - FailedDependency_424 = 424, - TooEarly_425 = 425, - UpgradeRequired_426 = 426, - PreconditionRequired_428 = 428, - TooManyRequests_429 = 429, - RequestHeaderFieldsTooLarge_431 = 431, - UnavailableForLegalReasons_451 = 451, +typedef std::multimap Params; +typedef std::smatch Match; - // Server error responses - InternalServerError_500 = 500, - NotImplemented_501 = 501, - BadGateway_502 = 502, - ServiceUnavailable_503 = 503, - GatewayTimeout_504 = 504, - HttpVersionNotSupported_505 = 505, - VariantAlsoNegotiates_506 = 506, - InsufficientStorage_507 = 507, - LoopDetected_508 = 508, - NotExtended_510 = 510, - NetworkAuthenticationRequired_511 = 511, -}; +typedef std::function ContentProducer; +typedef std::function ContentReceiver; +typedef std::function Progress; -using Headers = std::multimap; - -using Params = std::multimap; -using Match = std::smatch; - -using Progress = std::function; - -struct Response; -using ResponseHandler = std::function; - -struct MultipartFormData { - std::string name; - std::string content; +struct MultipartFile { std::string filename; std::string content_type; + size_t offset = 0; + size_t length = 0; }; -using MultipartFormDataItems = std::vector; -using MultipartFormDataMap = std::multimap; - -class DataSink { -public: - DataSink() : os(&sb_), sb_(*this) {} - - DataSink(const DataSink &) = delete; - DataSink &operator=(const DataSink &) = delete; - DataSink(DataSink &&) = delete; - DataSink &operator=(DataSink &&) = delete; - - std::function write; - std::function is_writable; - std::function done; - std::function done_with_trailer; - std::ostream os; - -private: - class data_sink_streambuf : public std::streambuf { - public: - explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {} - - protected: - std::streamsize xsputn(const char *s, std::streamsize n) override { - sink_.write(s, static_cast(n)); - return n; - } - - private: - DataSink &sink_; - }; - - data_sink_streambuf sb_; -}; - -using ContentProvider = - std::function; - -using ContentProviderWithoutLength = - std::function; - -using ContentProviderResourceReleaser = std::function; - -struct MultipartFormDataProvider { - std::string name; - ContentProviderWithoutLength provider; - std::string filename; - std::string content_type; -}; -using MultipartFormDataProviderItems = std::vector; - -using ContentReceiverWithProgress = - std::function; - -using ContentReceiver = - std::function; - -using MultipartContentHeader = - std::function; - -class ContentReader { -public: - using Reader = std::function; - using MultipartReader = std::function; - - ContentReader(Reader reader, MultipartReader multipart_reader) - : reader_(std::move(reader)), - multipart_reader_(std::move(multipart_reader)) {} - - bool operator()(MultipartContentHeader header, - ContentReceiver receiver) const { - return multipart_reader_(std::move(header), std::move(receiver)); - } - - bool operator()(ContentReceiver receiver) const { - return reader_(std::move(receiver)); - } - - Reader reader_; - MultipartReader multipart_reader_; -}; - -using Range = std::pair; -using Ranges = std::vector; +typedef std::multimap MultipartFiles; struct Request { + std::string version; std::string method; + std::string target; std::string path; Headers headers; std::string body; - - std::string remote_addr; - int remote_port = -1; - std::string local_addr; - int local_port = -1; - - // for server - std::string version; - std::string target; Params params; - MultipartFormDataMap files; - Ranges ranges; + MultipartFiles files; Match matches; - std::unordered_map path_params; - // for client - ResponseHandler response_handler; - ContentReceiverWithProgress content_receiver; - Progress progress; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - const SSL *ssl = nullptr; + const SSL *ssl; #endif - bool has_header(const std::string &key) const; - std::string get_header_value(const std::string &key, size_t id = 0) const; - uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; - size_t get_header_value_count(const std::string &key) const; - void set_header(const std::string &key, const std::string &val); + bool has_header(const char *key) const; + std::string get_header_value(const char *key, size_t id = 0) const; + size_t get_header_value_count(const char *key) const; + void set_header(const char *key, const char *val); - bool has_param(const std::string &key) const; - std::string get_param_value(const std::string &key, size_t id = 0) const; - size_t get_param_value_count(const std::string &key) const; + bool has_param(const char *key) const; + std::string get_param_value(const char *key, size_t id = 0) const; + size_t get_param_value_count(const char *key) const; - bool is_multipart_form_data() const; - - bool has_file(const std::string &key) const; - MultipartFormData get_file_value(const std::string &key) const; - std::vector get_file_values(const std::string &key) const; - - // private members... - size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT; - size_t content_length_ = 0; - ContentProvider content_provider_; - bool is_chunked_content_provider_ = false; - size_t authorization_count_ = 0; + bool has_file(const char *key) const; + MultipartFile get_file_value(const char *key) const; }; struct Response { std::string version; - int status = -1; - std::string reason; + int status; Headers headers; std::string body; - std::string location; // Redirect location - bool has_header(const std::string &key) const; - std::string get_header_value(const std::string &key, size_t id = 0) const; - uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; - size_t get_header_value_count(const std::string &key) const; - void set_header(const std::string &key, const std::string &val); + ContentProducer content_producer; + ContentReceiver content_receiver; + Progress progress; - void set_redirect(const std::string &url, int status = StatusCode::Found_302); - void set_content(const char *s, size_t n, const std::string &content_type); - void set_content(const std::string &s, const std::string &content_type); - void set_content(std::string &&s, const std::string &content_type); + bool has_header(const char *key) const; + std::string get_header_value(const char *key, size_t id = 0) const; + size_t get_header_value_count(const char *key) const; + void set_header(const char *key, const char *val); - void set_content_provider( - size_t length, const std::string &content_type, ContentProvider provider, - ContentProviderResourceReleaser resource_releaser = nullptr); + void set_redirect(const char *uri); + void set_content(const char *s, size_t n, const char *content_type); + void set_content(const std::string &s, const char *content_type); - void set_content_provider( - const std::string &content_type, ContentProviderWithoutLength provider, - ContentProviderResourceReleaser resource_releaser = nullptr); - - void set_chunked_content_provider( - const std::string &content_type, ContentProviderWithoutLength provider, - ContentProviderResourceReleaser resource_releaser = nullptr); - - Response() = default; - Response(const Response &) = default; - Response &operator=(const Response &) = default; - Response(Response &&) = default; - Response &operator=(Response &&) = default; - ~Response() { - if (content_provider_resource_releaser_) { - content_provider_resource_releaser_(content_provider_success_); - } - } - - // private members... - size_t content_length_ = 0; - ContentProvider content_provider_; - ContentProviderResourceReleaser content_provider_resource_releaser_; - bool is_chunked_content_provider_ = false; - bool content_provider_success_ = false; + Response() : status(-1) {} }; class Stream { public: - virtual ~Stream() = default; - - virtual bool is_readable() const = 0; - virtual bool is_writable() const = 0; - - virtual ssize_t read(char *ptr, size_t size) = 0; - virtual ssize_t write(const char *ptr, size_t size) = 0; - virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0; - virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0; - virtual socket_t socket() const = 0; + virtual ~Stream() {} + virtual int read(char *ptr, size_t size) = 0; + virtual int write(const char *ptr, size_t size1) = 0; + virtual int write(const char *ptr) = 0; + virtual std::string get_remote_addr() const = 0; template - ssize_t write_format(const char *fmt, const Args &...args); - ssize_t write(const char *ptr); - ssize_t write(const std::string &s); + void write_format(const char *fmt, const Args &... args); }; -class TaskQueue { +class SocketStream : public Stream { public: - TaskQueue() = default; - virtual ~TaskQueue() = default; + SocketStream(socket_t sock); + virtual ~SocketStream(); - virtual bool enqueue(std::function fn) = 0; - virtual void shutdown() = 0; - - virtual void on_idle() {} -}; - -class ThreadPool : public TaskQueue { -public: - explicit ThreadPool(size_t n, size_t mqr = 0) - : shutdown_(false), max_queued_requests_(mqr) { - while (n) { - threads_.emplace_back(worker(*this)); - n--; - } - } - - ThreadPool(const ThreadPool &) = delete; - ~ThreadPool() override = default; - - bool enqueue(std::function fn) override { - { - std::unique_lock lock(mutex_); - if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) { - return false; - } - jobs_.push_back(std::move(fn)); - } - - cond_.notify_one(); - return true; - } - - void shutdown() override { - // Stop all worker threads... - { - std::unique_lock lock(mutex_); - shutdown_ = true; - } - - cond_.notify_all(); - - // Join... - for (auto &t : threads_) { - t.join(); - } - } + virtual int read(char *ptr, size_t size); + virtual int write(const char *ptr, size_t size); + virtual int write(const char *ptr); + virtual std::string get_remote_addr() const; private: - struct worker { - explicit worker(ThreadPool &pool) : pool_(pool) {} - - void operator()() { - for (;;) { - std::function fn; - { - std::unique_lock lock(pool_.mutex_); - - pool_.cond_.wait( - lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; }); - - if (pool_.shutdown_ && pool_.jobs_.empty()) { break; } - - fn = std::move(pool_.jobs_.front()); - pool_.jobs_.pop_front(); - } - - assert(true == static_cast(fn)); - fn(); - } - } - - ThreadPool &pool_; - }; - friend struct worker; - - std::vector threads_; - std::list> jobs_; - - bool shutdown_; - size_t max_queued_requests_ = 0; - - std::condition_variable cond_; - std::mutex mutex_; + socket_t sock_; }; -using Logger = std::function; - -using SocketOptions = std::function; - -void default_socket_options(socket_t sock); - -const char *status_message(int status); - -std::string get_bearer_token_auth(const Request &req); - -namespace detail { - -class MatcherBase { +class BufferStream : public Stream { public: - virtual ~MatcherBase() = default; + BufferStream() {} + virtual ~BufferStream() {} - // Match request path and populate its matches and - virtual bool match(Request &request) const = 0; -}; + virtual int read(char *ptr, size_t size); + virtual int write(const char *ptr, size_t size); + virtual int write(const char *ptr); + virtual std::string get_remote_addr() const; -/** - * Captures parameters in request path and stores them in Request::path_params - * - * Capture name is a substring of a pattern from : to /. - * The rest of the pattern is matched agains the request path directly - * Parameters are captured starting from the next character after - * the end of the last matched static pattern fragment until the next /. - * - * Example pattern: - * "/path/fragments/:capture/more/fragments/:second_capture" - * Static fragments: - * "/path/fragments/", "more/fragments/" - * - * Given the following request path: - * "/path/fragments/:1/more/fragments/:2" - * the resulting capture will be - * {{"capture", "1"}, {"second_capture", "2"}} - */ -class PathParamsMatcher : public MatcherBase { -public: - PathParamsMatcher(const std::string &pattern); - - bool match(Request &request) const override; + const std::string &get_buffer() const; private: - static constexpr char marker = ':'; - // Treat segment separators as the end of path parameter capture - // Does not need to handle query parameters as they are parsed before path - // matching - static constexpr char separator = '/'; - - // Contains static path fragments to match against, excluding the '/' after - // path params - // Fragments are separated by path params - std::vector static_fragments_; - // Stores the names of the path parameters to be used as keys in the - // Request::path_params map - std::vector param_names_; + std::string buffer; }; -/** - * Performs std::regex_match on request path - * and stores the result in Request::matches - * - * Note that regex match is performed directly on the whole request. - * This means that wildcard patterns may match multiple path segments with /: - * "/begin/(.*)/end" will match both "/begin/middle/end" and "/begin/1/2/end". - */ -class RegexMatcher : public MatcherBase { -public: - RegexMatcher(const std::string &pattern) : regex_(pattern) {} - - bool match(Request &request) const override; - -private: - std::regex regex_; -}; - -ssize_t write_headers(Stream &strm, const Headers &headers); - -} // namespace detail - class Server { public: - using Handler = std::function; - - using ExceptionHandler = - std::function; - - enum class HandlerResponse { - Handled, - Unhandled, - }; - using HandlerWithResponse = - std::function; - - using HandlerWithContentReader = std::function; - - using Expect100ContinueHandler = - std::function; + typedef std::function Handler; + typedef std::function Logger; Server(); @@ -850,931 +240,201 @@ public: virtual bool is_valid() const; - Server &Get(const std::string &pattern, Handler handler); - Server &Post(const std::string &pattern, Handler handler); - Server &Post(const std::string &pattern, HandlerWithContentReader handler); - Server &Put(const std::string &pattern, Handler handler); - Server &Put(const std::string &pattern, HandlerWithContentReader handler); - Server &Patch(const std::string &pattern, Handler handler); - Server &Patch(const std::string &pattern, HandlerWithContentReader handler); - Server &Delete(const std::string &pattern, Handler handler); - Server &Delete(const std::string &pattern, HandlerWithContentReader handler); - Server &Options(const std::string &pattern, Handler handler); + Server &Get(const char *pattern, Handler handler); + Server &Post(const char *pattern, Handler handler); - bool set_base_dir(const std::string &dir, - const std::string &mount_point = std::string()); - bool set_mount_point(const std::string &mount_point, const std::string &dir, - Headers headers = Headers()); - bool remove_mount_point(const std::string &mount_point); - Server &set_file_extension_and_mimetype_mapping(const std::string &ext, - const std::string &mime); - Server &set_default_file_mimetype(const std::string &mime); - Server &set_file_request_handler(Handler handler); + Server &Put(const char *pattern, Handler handler); + Server &Patch(const char *pattern, Handler handler); + Server &Delete(const char *pattern, Handler handler); + Server &Options(const char *pattern, Handler handler); - Server &set_error_handler(HandlerWithResponse handler); - Server &set_error_handler(Handler handler); - Server &set_exception_handler(ExceptionHandler handler); - Server &set_pre_routing_handler(HandlerWithResponse handler); - Server &set_post_routing_handler(Handler handler); - - Server &set_expect_100_continue_handler(Expect100ContinueHandler handler); - Server &set_logger(Logger logger); - - Server &set_address_family(int family); - Server &set_tcp_nodelay(bool on); - Server &set_socket_options(SocketOptions socket_options); - - Server &set_default_headers(Headers headers); - Server & - set_header_writer(std::function const &writer); - - Server &set_keep_alive_max_count(size_t count); - Server &set_keep_alive_timeout(time_t sec); - - Server &set_read_timeout(time_t sec, time_t usec = 0); - template - Server &set_read_timeout(const std::chrono::duration &duration); - - Server &set_write_timeout(time_t sec, time_t usec = 0); - template - Server &set_write_timeout(const std::chrono::duration &duration); - - Server &set_idle_interval(time_t sec, time_t usec = 0); - template - Server &set_idle_interval(const std::chrono::duration &duration); - - Server &set_payload_max_length(size_t length); - - bool bind_to_port(const std::string &host, int port, int socket_flags = 0); - int bind_to_any_port(const std::string &host, int socket_flags = 0); - bool listen_after_bind(); - - bool listen(const std::string &host, int port, int socket_flags = 0); - - bool is_running() const; - void wait_until_ready() const; - void stop(); - - std::function new_task_queue; - -protected: - bool process_request(Stream &strm, bool close_connection, - bool &connection_closed, - const std::function &setup_request); - - std::atomic svr_sock_{INVALID_SOCKET}; - size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; - time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND; - time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; - time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; - time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; - time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; - time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND; - time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND; - size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH; - -private: - using Handlers = - std::vector, Handler>>; - using HandlersForContentReader = - std::vector, - HandlerWithContentReader>>; - - static std::unique_ptr - make_matcher(const std::string &pattern); - - socket_t create_server_socket(const std::string &host, int port, - int socket_flags, - SocketOptions socket_options) const; - int bind_internal(const std::string &host, int port, int socket_flags); - bool listen_internal(); - - bool routing(Request &req, Response &res, Stream &strm); - bool handle_file_request(const Request &req, Response &res, - bool head = false); - bool dispatch_request(Request &req, Response &res, - const Handlers &handlers) const; - bool dispatch_request_for_content_reader( - Request &req, Response &res, ContentReader content_reader, - const HandlersForContentReader &handlers) const; - - bool parse_request_line(const char *s, Request &req) const; - void apply_ranges(const Request &req, Response &res, - std::string &content_type, std::string &boundary) const; - bool write_response(Stream &strm, bool close_connection, Request &req, - Response &res); - bool write_response_with_content(Stream &strm, bool close_connection, - const Request &req, Response &res); - bool write_response_core(Stream &strm, bool close_connection, - const Request &req, Response &res, - bool need_apply_ranges); - bool write_content_with_provider(Stream &strm, const Request &req, - Response &res, const std::string &boundary, - const std::string &content_type); - bool read_content(Stream &strm, Request &req, Response &res); - bool - read_content_with_content_receiver(Stream &strm, Request &req, Response &res, - ContentReceiver receiver, - MultipartContentHeader multipart_header, - ContentReceiver multipart_receiver); - bool read_content_core(Stream &strm, Request &req, Response &res, - ContentReceiver receiver, - MultipartContentHeader multipart_header, - ContentReceiver multipart_receiver) const; - - virtual bool process_and_close_socket(socket_t sock); - - std::atomic is_running_{false}; - std::atomic done_{false}; - - struct MountPointEntry { - std::string mount_point; - std::string base_dir; - Headers headers; - }; - std::vector base_dirs_; - std::map file_extension_and_mimetype_map_; - std::string default_file_mimetype_ = "application/octet-stream"; - Handler file_request_handler_; - - Handlers get_handlers_; - Handlers post_handlers_; - HandlersForContentReader post_handlers_for_content_reader_; - Handlers put_handlers_; - HandlersForContentReader put_handlers_for_content_reader_; - Handlers patch_handlers_; - HandlersForContentReader patch_handlers_for_content_reader_; - Handlers delete_handlers_; - HandlersForContentReader delete_handlers_for_content_reader_; - Handlers options_handlers_; - - HandlerWithResponse error_handler_; - ExceptionHandler exception_handler_; - HandlerWithResponse pre_routing_handler_; - Handler post_routing_handler_; - Expect100ContinueHandler expect_100_continue_handler_; - - Logger logger_; - - int address_family_ = AF_UNSPEC; - bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; - SocketOptions socket_options_ = default_socket_options; - - Headers default_headers_; - std::function header_writer_ = - detail::write_headers; -}; - -enum class Error { - Success = 0, - Unknown, - Connection, - BindIPAddress, - Read, - Write, - ExceedRedirectCount, - Canceled, - SSLConnection, - SSLLoadingCerts, - SSLServerVerification, - UnsupportedMultipartBoundaryChars, - Compression, - ConnectionTimeout, - ProxyConnection, - - // For internal use only - SSLPeerCouldBeClosed_, -}; - -std::string to_string(Error error); - -std::ostream &operator<<(std::ostream &os, const Error &obj); - -class Result { -public: - Result() = default; - Result(std::unique_ptr &&res, Error err, - Headers &&request_headers = Headers{}) - : res_(std::move(res)), err_(err), - request_headers_(std::move(request_headers)) {} - // Response - operator bool() const { return res_ != nullptr; } - bool operator==(std::nullptr_t) const { return res_ == nullptr; } - bool operator!=(std::nullptr_t) const { return res_ != nullptr; } - const Response &value() const { return *res_; } - Response &value() { return *res_; } - const Response &operator*() const { return *res_; } - Response &operator*() { return *res_; } - const Response *operator->() const { return res_.get(); } - Response *operator->() { return res_.get(); } - - // Error - Error error() const { return err_; } - - // Request Headers - bool has_request_header(const std::string &key) const; - std::string get_request_header_value(const std::string &key, - size_t id = 0) const; - uint64_t get_request_header_value_u64(const std::string &key, - size_t id = 0) const; - size_t get_request_header_value_count(const std::string &key) const; - -private: - std::unique_ptr res_; - Error err_ = Error::Unknown; - Headers request_headers_; -}; - -class ClientImpl { -public: - explicit ClientImpl(const std::string &host); - - explicit ClientImpl(const std::string &host, int port); - - explicit ClientImpl(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path); - - virtual ~ClientImpl(); - - virtual bool is_valid() const; - - Result Get(const std::string &path); - Result Get(const std::string &path, const Headers &headers); - Result Get(const std::string &path, Progress progress); - Result Get(const std::string &path, const Headers &headers, - Progress progress); - Result Get(const std::string &path, ContentReceiver content_receiver); - Result Get(const std::string &path, const Headers &headers, - ContentReceiver content_receiver); - Result Get(const std::string &path, ContentReceiver content_receiver, - Progress progress); - Result Get(const std::string &path, const Headers &headers, - ContentReceiver content_receiver, Progress progress); - Result Get(const std::string &path, ResponseHandler response_handler, - ContentReceiver content_receiver); - Result Get(const std::string &path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver); - Result Get(const std::string &path, ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress); - Result Get(const std::string &path, const Headers &headers, - ResponseHandler response_handler, ContentReceiver content_receiver, - Progress progress); - - Result Get(const std::string &path, const Params ¶ms, - const Headers &headers, Progress progress = nullptr); - Result Get(const std::string &path, const Params ¶ms, - const Headers &headers, ContentReceiver content_receiver, - Progress progress = nullptr); - Result Get(const std::string &path, const Params ¶ms, - const Headers &headers, ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress = nullptr); - - Result Head(const std::string &path); - Result Head(const std::string &path, const Headers &headers); - - Result Post(const std::string &path); - Result Post(const std::string &path, const Headers &headers); - Result Post(const std::string &path, const char *body, size_t content_length, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, const char *body, - size_t content_length, const std::string &content_type); - Result Post(const std::string &path, const std::string &body, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - Result Post(const std::string &path, size_t content_length, - ContentProvider content_provider, - const std::string &content_type); - Result Post(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, - size_t content_length, ContentProvider content_provider, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Post(const std::string &path, const Params ¶ms); - Result Post(const std::string &path, const Headers &headers, - const Params ¶ms); - Result Post(const std::string &path, const MultipartFormDataItems &items); - Result Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items); - Result Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, const std::string &boundary); - Result Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items); - - Result Put(const std::string &path); - Result Put(const std::string &path, const char *body, size_t content_length, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, const char *body, - size_t content_length, const std::string &content_type); - Result Put(const std::string &path, const std::string &body, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - Result Put(const std::string &path, size_t content_length, - ContentProvider content_provider, const std::string &content_type); - Result Put(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, - size_t content_length, ContentProvider content_provider, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Put(const std::string &path, const Params ¶ms); - Result Put(const std::string &path, const Headers &headers, - const Params ¶ms); - Result Put(const std::string &path, const MultipartFormDataItems &items); - Result Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items); - Result Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, const std::string &boundary); - Result Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items); - - Result Patch(const std::string &path); - Result Patch(const std::string &path, const char *body, size_t content_length, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type); - Result Patch(const std::string &path, const std::string &body, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - Result Patch(const std::string &path, size_t content_length, - ContentProvider content_provider, - const std::string &content_type); - Result Patch(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - size_t content_length, ContentProvider content_provider, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - - Result Delete(const std::string &path); - Result Delete(const std::string &path, const Headers &headers); - Result Delete(const std::string &path, const char *body, - size_t content_length, const std::string &content_type); - Result Delete(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type); - Result Delete(const std::string &path, const std::string &body, - const std::string &content_type); - Result Delete(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - - Result Options(const std::string &path); - Result Options(const std::string &path, const Headers &headers); - - bool send(Request &req, Response &res, Error &error); - Result send(const Request &req); - - void stop(); - - std::string host() const; - int port() const; - - size_t is_socket_open() const; - socket_t socket() const; - - void set_hostname_addr_map(std::map addr_map); - - void set_default_headers(Headers headers); - - void - set_header_writer(std::function const &writer); - - void set_address_family(int family); - void set_tcp_nodelay(bool on); - void set_socket_options(SocketOptions socket_options); - - void set_connection_timeout(time_t sec, time_t usec = 0); - template - void - set_connection_timeout(const std::chrono::duration &duration); - - void set_read_timeout(time_t sec, time_t usec = 0); - template - void set_read_timeout(const std::chrono::duration &duration); - - void set_write_timeout(time_t sec, time_t usec = 0); - template - void set_write_timeout(const std::chrono::duration &duration); - - void set_basic_auth(const std::string &username, const std::string &password); - void set_bearer_token_auth(const std::string &token); -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_digest_auth(const std::string &username, - const std::string &password); -#endif - - void set_keep_alive(bool on); - void set_follow_location(bool on); - - void set_url_encode(bool on); - - void set_compress(bool on); - - void set_decompress(bool on); - - void set_interface(const std::string &intf); - - void set_proxy(const std::string &host, int port); - void set_proxy_basic_auth(const std::string &username, - const std::string &password); - void set_proxy_bearer_token_auth(const std::string &token); -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_proxy_digest_auth(const std::string &username, - const std::string &password); -#endif - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_ca_cert_path(const std::string &ca_cert_file_path, - const std::string &ca_cert_dir_path = std::string()); - void set_ca_cert_store(X509_STORE *ca_cert_store); - X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const; -#endif - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void enable_server_certificate_verification(bool enabled); -#endif + bool set_base_dir(const char *path); + void set_error_handler(Handler handler); void set_logger(Logger logger); + void set_keep_alive_max_count(size_t count); + void set_payload_max_length(uint64_t length); + + int bind_to_any_port(const char *host, int socket_flags = 0); + bool listen_after_bind(); + + bool listen(const char *host, int port, int socket_flags = 0); + + bool is_running() const; + void stop(); + protected: - struct Socket { - socket_t sock = INVALID_SOCKET; -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - SSL *ssl = nullptr; -#endif + bool process_request(Stream &strm, bool last_connection, + bool &connection_close, + std::function setup_request = nullptr); - bool is_open() const { return sock != INVALID_SOCKET; } - }; - - virtual bool create_and_connect_socket(Socket &socket, Error &error); - - // All of: - // shutdown_ssl - // shutdown_socket - // close_socket - // should ONLY be called when socket_mutex_ is locked. - // Also, shutdown_ssl and close_socket should also NOT be called concurrently - // with a DIFFERENT thread sending requests using that socket. - virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully); - void shutdown_socket(Socket &socket) const; - void close_socket(Socket &socket); - - bool process_request(Stream &strm, Request &req, Response &res, - bool close_connection, Error &error); - - bool write_content_with_provider(Stream &strm, const Request &req, - Error &error) const; - - void copy_settings(const ClientImpl &rhs); - - // Socket endpoint information - const std::string host_; - const int port_; - const std::string host_and_port_; - - // Current open socket - Socket socket_; - mutable std::mutex socket_mutex_; - std::recursive_mutex request_mutex_; - - // These are all protected under socket_mutex - size_t socket_requests_in_flight_ = 0; - std::thread::id socket_requests_are_from_thread_ = std::thread::id(); - bool socket_should_be_closed_when_request_is_done_ = false; - - // Hostname-IP map - std::map addr_map_; - - // Default headers - Headers default_headers_; - - // Header writer - std::function header_writer_ = - detail::write_headers; - - // Settings - std::string client_cert_path_; - std::string client_key_path_; - - time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND; - time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND; - time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; - time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; - time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; - time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; - - std::string basic_auth_username_; - std::string basic_auth_password_; - std::string bearer_token_auth_token_; -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - std::string digest_auth_username_; - std::string digest_auth_password_; -#endif - - bool keep_alive_ = false; - bool follow_location_ = false; - - bool url_encode_ = true; - - int address_family_ = AF_UNSPEC; - bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; - SocketOptions socket_options_ = nullptr; - - bool compress_ = false; - bool decompress_ = true; - - std::string interface_; - - std::string proxy_host_; - int proxy_port_ = -1; - - std::string proxy_basic_auth_username_; - std::string proxy_basic_auth_password_; - std::string proxy_bearer_token_auth_token_; -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - std::string proxy_digest_auth_username_; - std::string proxy_digest_auth_password_; -#endif - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - std::string ca_cert_file_path_; - std::string ca_cert_dir_path_; - - X509_STORE *ca_cert_store_ = nullptr; -#endif - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - bool server_certificate_verification_ = true; -#endif - - Logger logger_; + size_t keep_alive_max_count_; + size_t payload_max_length_; private: - bool send_(Request &req, Response &res, Error &error); - Result send_(Request &&req); + typedef std::vector> Handlers; - socket_t create_client_socket(Error &error) const; - bool read_response_line(Stream &strm, const Request &req, - Response &res) const; - bool write_request(Stream &strm, Request &req, bool close_connection, - Error &error); - bool redirect(Request &req, Response &res, Error &error); - bool handle_request(Stream &strm, Request &req, Response &res, - bool close_connection, Error &error); - std::unique_ptr send_with_content_provider( - Request &req, const char *body, size_t content_length, - ContentProvider content_provider, - ContentProviderWithoutLength content_provider_without_length, - const std::string &content_type, Error &error); - Result send_with_content_provider( - const std::string &method, const std::string &path, - const Headers &headers, const char *body, size_t content_length, - ContentProvider content_provider, - ContentProviderWithoutLength content_provider_without_length, - const std::string &content_type); - ContentProviderWithoutLength get_multipart_content_provider( - const std::string &boundary, const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items) const; + socket_t create_server_socket(const char *host, int port, + int socket_flags) const; + int bind_internal(const char *host, int port, int socket_flags); + bool listen_internal(); - std::string adjust_host_string(const std::string &host) const; + bool routing(Request &req, Response &res); + bool handle_file_request(Request &req, Response &res); + bool dispatch_request(Request &req, Response &res, Handlers &handlers); - virtual bool process_socket(const Socket &socket, - std::function callback); - virtual bool is_ssl() const; + bool parse_request_line(const char *s, Request &req); + void write_response(Stream &strm, bool last_connection, const Request &req, + Response &res); + + virtual bool read_and_close_socket(socket_t sock); + + std::atomic is_running_; + std::atomic svr_sock_; + std::string base_dir_; + Handlers get_handlers_; + Handlers post_handlers_; + Handlers put_handlers_; + Handlers patch_handlers_; + Handlers delete_handlers_; + Handlers options_handlers_; + Handler error_handler_; + Logger logger_; + + // TODO: Use thread pool... + std::mutex running_threads_mutex_; + int running_threads_; }; class Client { public: - // Universal interface - explicit Client(const std::string &scheme_host_port); + Client(const char *host, int port = 80, time_t timeout_sec = 300); - explicit Client(const std::string &scheme_host_port, - const std::string &client_cert_path, - const std::string &client_key_path); + virtual ~Client(); - // HTTP only interface - explicit Client(const std::string &host, int port); + virtual bool is_valid() const; - explicit Client(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path); + std::shared_ptr Get(const char *path, Progress progress = nullptr); + std::shared_ptr Get(const char *path, const Headers &headers, + Progress progress = nullptr); - Client(Client &&) = default; + std::shared_ptr Get(const char *path, + ContentReceiver content_receiver, + Progress progress = nullptr); + std::shared_ptr Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, + Progress progress = nullptr); - ~Client(); + std::shared_ptr Head(const char *path); + std::shared_ptr Head(const char *path, const Headers &headers); - bool is_valid() const; + std::shared_ptr Post(const char *path, const std::string &body, + const char *content_type); + std::shared_ptr Post(const char *path, const Headers &headers, + const std::string &body, + const char *content_type); - Result Get(const std::string &path); - Result Get(const std::string &path, const Headers &headers); - Result Get(const std::string &path, Progress progress); - Result Get(const std::string &path, const Headers &headers, - Progress progress); - Result Get(const std::string &path, ContentReceiver content_receiver); - Result Get(const std::string &path, const Headers &headers, - ContentReceiver content_receiver); - Result Get(const std::string &path, ContentReceiver content_receiver, - Progress progress); - Result Get(const std::string &path, const Headers &headers, - ContentReceiver content_receiver, Progress progress); - Result Get(const std::string &path, ResponseHandler response_handler, - ContentReceiver content_receiver); - Result Get(const std::string &path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver); - Result Get(const std::string &path, const Headers &headers, - ResponseHandler response_handler, ContentReceiver content_receiver, - Progress progress); - Result Get(const std::string &path, ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress); + std::shared_ptr Post(const char *path, const Params ¶ms); + std::shared_ptr Post(const char *path, const Headers &headers, + const Params ¶ms); - Result Get(const std::string &path, const Params ¶ms, - const Headers &headers, Progress progress = nullptr); - Result Get(const std::string &path, const Params ¶ms, - const Headers &headers, ContentReceiver content_receiver, - Progress progress = nullptr); - Result Get(const std::string &path, const Params ¶ms, - const Headers &headers, ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress = nullptr); + std::shared_ptr Put(const char *path, const std::string &body, + const char *content_type); + std::shared_ptr Put(const char *path, const Headers &headers, + const std::string &body, + const char *content_type); - Result Head(const std::string &path); - Result Head(const std::string &path, const Headers &headers); + std::shared_ptr Patch(const char *path, const std::string &body, + const char *content_type); + std::shared_ptr Patch(const char *path, const Headers &headers, + const std::string &body, + const char *content_type); - Result Post(const std::string &path); - Result Post(const std::string &path, const Headers &headers); - Result Post(const std::string &path, const char *body, size_t content_length, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, const char *body, - size_t content_length, const std::string &content_type); - Result Post(const std::string &path, const std::string &body, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - Result Post(const std::string &path, size_t content_length, - ContentProvider content_provider, - const std::string &content_type); - Result Post(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, - size_t content_length, ContentProvider content_provider, - const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Post(const std::string &path, const Params ¶ms); - Result Post(const std::string &path, const Headers &headers, - const Params ¶ms); - Result Post(const std::string &path, const MultipartFormDataItems &items); - Result Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items); - Result Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, const std::string &boundary); - Result Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items); + std::shared_ptr Delete(const char *path, + const std::string &body = std::string(), + const char *content_type = nullptr); + std::shared_ptr Delete(const char *path, const Headers &headers, + const std::string &body = std::string(), + const char *content_type = nullptr); - Result Put(const std::string &path); - Result Put(const std::string &path, const char *body, size_t content_length, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, const char *body, - size_t content_length, const std::string &content_type); - Result Put(const std::string &path, const std::string &body, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - Result Put(const std::string &path, size_t content_length, - ContentProvider content_provider, const std::string &content_type); - Result Put(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, - size_t content_length, ContentProvider content_provider, - const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Put(const std::string &path, const Params ¶ms); - Result Put(const std::string &path, const Headers &headers, - const Params ¶ms); - Result Put(const std::string &path, const MultipartFormDataItems &items); - Result Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items); - Result Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, const std::string &boundary); - Result Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items); + std::shared_ptr Options(const char *path); + std::shared_ptr Options(const char *path, const Headers &headers); - Result Patch(const std::string &path); - Result Patch(const std::string &path, const char *body, size_t content_length, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type); - Result Patch(const std::string &path, const std::string &body, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); - Result Patch(const std::string &path, size_t content_length, - ContentProvider content_provider, - const std::string &content_type); - Result Patch(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - size_t content_length, ContentProvider content_provider, - const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type); + bool send(Request &req, Response &res); - Result Delete(const std::string &path); - Result Delete(const std::string &path, const Headers &headers); - Result Delete(const std::string &path, const char *body, - size_t content_length, const std::string &content_type); - Result Delete(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type); - Result Delete(const std::string &path, const std::string &body, - const std::string &content_type); - Result Delete(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type); +protected: + bool process_request(Stream &strm, Request &req, Response &res, + bool &connection_close); - Result Options(const std::string &path); - Result Options(const std::string &path, const Headers &headers); - - bool send(Request &req, Response &res, Error &error); - Result send(const Request &req); - - void stop(); - - std::string host() const; - int port() const; - - size_t is_socket_open() const; - socket_t socket() const; - - void set_hostname_addr_map(std::map addr_map); - - void set_default_headers(Headers headers); - - void - set_header_writer(std::function const &writer); - - void set_address_family(int family); - void set_tcp_nodelay(bool on); - void set_socket_options(SocketOptions socket_options); - - void set_connection_timeout(time_t sec, time_t usec = 0); - template - void - set_connection_timeout(const std::chrono::duration &duration); - - void set_read_timeout(time_t sec, time_t usec = 0); - template - void set_read_timeout(const std::chrono::duration &duration); - - void set_write_timeout(time_t sec, time_t usec = 0); - template - void set_write_timeout(const std::chrono::duration &duration); - - void set_basic_auth(const std::string &username, const std::string &password); - void set_bearer_token_auth(const std::string &token); -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_digest_auth(const std::string &username, - const std::string &password); -#endif - - void set_keep_alive(bool on); - void set_follow_location(bool on); - - void set_url_encode(bool on); - - void set_compress(bool on); - - void set_decompress(bool on); - - void set_interface(const std::string &intf); - - void set_proxy(const std::string &host, int port); - void set_proxy_basic_auth(const std::string &username, - const std::string &password); - void set_proxy_bearer_token_auth(const std::string &token); -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_proxy_digest_auth(const std::string &username, - const std::string &password); -#endif - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void enable_server_certificate_verification(bool enabled); -#endif - - void set_logger(Logger logger); - - // SSL -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_ca_cert_path(const std::string &ca_cert_file_path, - const std::string &ca_cert_dir_path = std::string()); - - void set_ca_cert_store(X509_STORE *ca_cert_store); - void load_ca_cert_store(const char *ca_cert, std::size_t size); - - long get_openssl_verify_result() const; - - SSL_CTX *ssl_context() const; -#endif + const std::string host_; + const int port_; + time_t timeout_sec_; + const std::string host_and_port_; private: - std::unique_ptr cli_; + socket_t create_client_socket() const; + bool read_response_line(Stream &strm, Response &res); + void write_request(Stream &strm, Request &req); -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - bool is_ssl_ = false; -#endif + virtual bool read_and_close_socket(socket_t sock, Request &req, + Response &res); + virtual bool is_ssl() const; }; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT +class SSLSocketStream : public Stream { +public: + SSLSocketStream(socket_t sock, SSL *ssl); + virtual ~SSLSocketStream(); + + virtual int read(char *ptr, size_t size); + virtual int write(const char *ptr, size_t size); + virtual int write(const char *ptr); + virtual std::string get_remote_addr() const; + +private: + socket_t sock_; + SSL *ssl_; +}; + class SSLServer : public Server { public: SSLServer(const char *cert_path, const char *private_key_path, const char *client_ca_cert_file_path = nullptr, - const char *client_ca_cert_dir_path = nullptr, - const char *private_key_password = nullptr); + const char *client_ca_cert_dir_path = nullptr); - SSLServer(X509 *cert, EVP_PKEY *private_key, - X509_STORE *client_ca_cert_store = nullptr); + virtual ~SSLServer(); - SSLServer( - const std::function &setup_ssl_ctx_callback); - - ~SSLServer() override; - - bool is_valid() const override; - - SSL_CTX *ssl_context() const; + virtual bool is_valid() const; private: - bool process_and_close_socket(socket_t sock) override; + virtual bool read_and_close_socket(socket_t sock); SSL_CTX *ctx_; std::mutex ctx_mutex_; }; -class SSLClient : public ClientImpl { +class SSLClient : public Client { public: - explicit SSLClient(const std::string &host); + SSLClient(const char *host, int port = 443, time_t timeout_sec = 300, + const char *client_cert_path = nullptr, + const char *client_key_path = nullptr); - explicit SSLClient(const std::string &host, int port); + virtual ~SSLClient(); - explicit SSLClient(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path); + virtual bool is_valid() const; - explicit SSLClient(const std::string &host, int port, X509 *client_cert, - EVP_PKEY *client_key); - - ~SSLClient() override; - - bool is_valid() const override; - - void set_ca_cert_store(X509_STORE *ca_cert_store); - void load_ca_cert_store(const char *ca_cert, std::size_t size); + void set_ca_cert_path(const char *ca_ceert_file_path, + const char *ca_cert_dir_path = nullptr); + void enable_server_certificate_verification(bool enabled); long get_openssl_verify_result() const; - SSL_CTX *ssl_context() const; - private: - bool create_and_connect_socket(Socket &socket, Error &error) override; - void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override; - void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully); - - bool process_socket(const Socket &socket, - std::function callback) override; - bool is_ssl() const override; - - bool connect_with_proxy(Socket &sock, Response &res, bool &success, - Error &error); - bool initialize_ssl(Socket &socket, Error &error); - - bool load_certs(); + virtual bool read_and_close_socket(socket_t sock, Request &req, + Response &res); + virtual bool is_ssl() const; bool verify_host(X509 *server_cert) const; bool verify_host_with_subject_alt_name(X509 *server_cert) const; @@ -1783,508 +443,17 @@ private: SSL_CTX *ctx_; std::mutex ctx_mutex_; - std::once_flag initialize_cert_; - std::vector host_components_; - + std::string ca_cert_file_path_; + std::string ca_cert_dir_path_; + bool server_certificate_verification_ = false; long verify_result_ = 0; - - friend class ClientImpl; }; #endif /* - * Implementation of template methods. + * Implementation */ - -namespace detail { - -template -inline void duration_to_sec_and_usec(const T &duration, U callback) { - auto sec = std::chrono::duration_cast(duration).count(); - auto usec = std::chrono::duration_cast( - duration - std::chrono::seconds(sec)) - .count(); - callback(static_cast(sec), static_cast(usec)); -} - -inline uint64_t get_header_value_u64(const Headers &headers, - const std::string &key, size_t id, - uint64_t def) { - auto rng = headers.equal_range(key); - auto it = rng.first; - std::advance(it, static_cast(id)); - if (it != rng.second) { - return std::strtoull(it->second.data(), nullptr, 10); - } - return def; -} - -} // namespace detail - -inline uint64_t Request::get_header_value_u64(const std::string &key, - size_t id) const { - return detail::get_header_value_u64(headers, key, id, 0); -} - -inline uint64_t Response::get_header_value_u64(const std::string &key, - size_t id) const { - return detail::get_header_value_u64(headers, key, id, 0); -} - -template -inline ssize_t Stream::write_format(const char *fmt, const Args &...args) { - const auto bufsiz = 2048; - std::array buf{}; - - auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...); - if (sn <= 0) { return sn; } - - auto n = static_cast(sn); - - if (n >= buf.size() - 1) { - std::vector glowable_buf(buf.size()); - - while (n >= glowable_buf.size() - 1) { - glowable_buf.resize(glowable_buf.size() * 2); - n = static_cast( - snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...)); - } - return write(&glowable_buf[0], n); - } else { - return write(buf.data(), n); - } -} - -inline void default_socket_options(socket_t sock) { - int yes = 1; -#ifdef _WIN32 - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - reinterpret_cast(&yes), sizeof(yes)); - setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, - reinterpret_cast(&yes), sizeof(yes)); -#else -#ifdef SO_REUSEPORT - setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, - reinterpret_cast(&yes), sizeof(yes)); -#else - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - reinterpret_cast(&yes), sizeof(yes)); -#endif -#endif -} - -inline const char *status_message(int status) { - switch (status) { - case StatusCode::Continue_100: return "Continue"; - case StatusCode::SwitchingProtocol_101: return "Switching Protocol"; - case StatusCode::Processing_102: return "Processing"; - case StatusCode::EarlyHints_103: return "Early Hints"; - case StatusCode::OK_200: return "OK"; - case StatusCode::Created_201: return "Created"; - case StatusCode::Accepted_202: return "Accepted"; - case StatusCode::NonAuthoritativeInformation_203: - return "Non-Authoritative Information"; - case StatusCode::NoContent_204: return "No Content"; - case StatusCode::ResetContent_205: return "Reset Content"; - case StatusCode::PartialContent_206: return "Partial Content"; - case StatusCode::MultiStatus_207: return "Multi-Status"; - case StatusCode::AlreadyReported_208: return "Already Reported"; - case StatusCode::IMUsed_226: return "IM Used"; - case StatusCode::MultipleChoices_300: return "Multiple Choices"; - case StatusCode::MovedPermanently_301: return "Moved Permanently"; - case StatusCode::Found_302: return "Found"; - case StatusCode::SeeOther_303: return "See Other"; - case StatusCode::NotModified_304: return "Not Modified"; - case StatusCode::UseProxy_305: return "Use Proxy"; - case StatusCode::unused_306: return "unused"; - case StatusCode::TemporaryRedirect_307: return "Temporary Redirect"; - case StatusCode::PermanentRedirect_308: return "Permanent Redirect"; - case StatusCode::BadRequest_400: return "Bad Request"; - case StatusCode::Unauthorized_401: return "Unauthorized"; - case StatusCode::PaymentRequired_402: return "Payment Required"; - case StatusCode::Forbidden_403: return "Forbidden"; - case StatusCode::NotFound_404: return "Not Found"; - case StatusCode::MethodNotAllowed_405: return "Method Not Allowed"; - case StatusCode::NotAcceptable_406: return "Not Acceptable"; - case StatusCode::ProxyAuthenticationRequired_407: - return "Proxy Authentication Required"; - case StatusCode::RequestTimeout_408: return "Request Timeout"; - case StatusCode::Conflict_409: return "Conflict"; - case StatusCode::Gone_410: return "Gone"; - case StatusCode::LengthRequired_411: return "Length Required"; - case StatusCode::PreconditionFailed_412: return "Precondition Failed"; - case StatusCode::PayloadTooLarge_413: return "Payload Too Large"; - case StatusCode::UriTooLong_414: return "URI Too Long"; - case StatusCode::UnsupportedMediaType_415: return "Unsupported Media Type"; - case StatusCode::RangeNotSatisfiable_416: return "Range Not Satisfiable"; - case StatusCode::ExpectationFailed_417: return "Expectation Failed"; - case StatusCode::ImATeapot_418: return "I'm a teapot"; - case StatusCode::MisdirectedRequest_421: return "Misdirected Request"; - case StatusCode::UnprocessableContent_422: return "Unprocessable Content"; - case StatusCode::Locked_423: return "Locked"; - case StatusCode::FailedDependency_424: return "Failed Dependency"; - case StatusCode::TooEarly_425: return "Too Early"; - case StatusCode::UpgradeRequired_426: return "Upgrade Required"; - case StatusCode::PreconditionRequired_428: return "Precondition Required"; - case StatusCode::TooManyRequests_429: return "Too Many Requests"; - case StatusCode::RequestHeaderFieldsTooLarge_431: - return "Request Header Fields Too Large"; - case StatusCode::UnavailableForLegalReasons_451: - return "Unavailable For Legal Reasons"; - case StatusCode::NotImplemented_501: return "Not Implemented"; - case StatusCode::BadGateway_502: return "Bad Gateway"; - case StatusCode::ServiceUnavailable_503: return "Service Unavailable"; - case StatusCode::GatewayTimeout_504: return "Gateway Timeout"; - case StatusCode::HttpVersionNotSupported_505: - return "HTTP Version Not Supported"; - case StatusCode::VariantAlsoNegotiates_506: return "Variant Also Negotiates"; - case StatusCode::InsufficientStorage_507: return "Insufficient Storage"; - case StatusCode::LoopDetected_508: return "Loop Detected"; - case StatusCode::NotExtended_510: return "Not Extended"; - case StatusCode::NetworkAuthenticationRequired_511: - return "Network Authentication Required"; - - default: - case StatusCode::InternalServerError_500: return "Internal Server Error"; - } -} - -inline std::string get_bearer_token_auth(const Request &req) { - if (req.has_header("Authorization")) { - static std::string BearerHeaderPrefix = "Bearer "; - return req.get_header_value("Authorization") - .substr(BearerHeaderPrefix.length()); - } - return ""; -} - -template -inline Server & -Server::set_read_timeout(const std::chrono::duration &duration) { - detail::duration_to_sec_and_usec( - duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); }); - return *this; -} - -template -inline Server & -Server::set_write_timeout(const std::chrono::duration &duration) { - detail::duration_to_sec_and_usec( - duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); }); - return *this; -} - -template -inline Server & -Server::set_idle_interval(const std::chrono::duration &duration) { - detail::duration_to_sec_and_usec( - duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); }); - return *this; -} - -inline std::string to_string(const Error error) { - switch (error) { - case Error::Success: return "Success (no error)"; - case Error::Connection: return "Could not establish connection"; - case Error::BindIPAddress: return "Failed to bind IP address"; - case Error::Read: return "Failed to read connection"; - case Error::Write: return "Failed to write connection"; - case Error::ExceedRedirectCount: return "Maximum redirect count exceeded"; - case Error::Canceled: return "Connection handling canceled"; - case Error::SSLConnection: return "SSL connection failed"; - case Error::SSLLoadingCerts: return "SSL certificate loading failed"; - case Error::SSLServerVerification: return "SSL server verification failed"; - case Error::UnsupportedMultipartBoundaryChars: - return "Unsupported HTTP multipart boundary characters"; - case Error::Compression: return "Compression failed"; - case Error::ConnectionTimeout: return "Connection timed out"; - case Error::ProxyConnection: return "Proxy connection failed"; - case Error::Unknown: return "Unknown"; - default: break; - } - - return "Invalid"; -} - -inline std::ostream &operator<<(std::ostream &os, const Error &obj) { - os << to_string(obj); - os << " (" << static_cast::type>(obj) << ')'; - return os; -} - -inline uint64_t Result::get_request_header_value_u64(const std::string &key, - size_t id) const { - return detail::get_header_value_u64(request_headers_, key, id, 0); -} - -template -inline void ClientImpl::set_connection_timeout( - const std::chrono::duration &duration) { - detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) { - set_connection_timeout(sec, usec); - }); -} - -template -inline void ClientImpl::set_read_timeout( - const std::chrono::duration &duration) { - detail::duration_to_sec_and_usec( - duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); }); -} - -template -inline void ClientImpl::set_write_timeout( - const std::chrono::duration &duration) { - detail::duration_to_sec_and_usec( - duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); }); -} - -template -inline void Client::set_connection_timeout( - const std::chrono::duration &duration) { - cli_->set_connection_timeout(duration); -} - -template -inline void -Client::set_read_timeout(const std::chrono::duration &duration) { - cli_->set_read_timeout(duration); -} - -template -inline void -Client::set_write_timeout(const std::chrono::duration &duration) { - cli_->set_write_timeout(duration); -} - -/* - * Forward declarations and types that will be part of the .h file if split into - * .h + .cc. - */ - -std::string hosted_at(const std::string &hostname); - -void hosted_at(const std::string &hostname, std::vector &addrs); - -std::string append_query_params(const std::string &path, const Params ¶ms); - -std::pair make_range_header(const Ranges &ranges); - -std::pair -make_basic_authentication_header(const std::string &username, - const std::string &password, - bool is_proxy = false); - -namespace detail { - -std::string encode_query_param(const std::string &value); - -std::string decode_url(const std::string &s, bool convert_plus_to_space); - -void read_file(const std::string &path, std::string &out); - -std::string trim_copy(const std::string &s); - -void split(const char *b, const char *e, char d, - std::function fn); - -void split(const char *b, const char *e, char d, size_t m, - std::function fn); - -bool process_client_socket(socket_t sock, time_t read_timeout_sec, - time_t read_timeout_usec, time_t write_timeout_sec, - time_t write_timeout_usec, - std::function callback); - -socket_t create_client_socket( - const std::string &host, const std::string &ip, int port, - int address_family, bool tcp_nodelay, SocketOptions socket_options, - time_t connection_timeout_sec, time_t connection_timeout_usec, - time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, - time_t write_timeout_usec, const std::string &intf, Error &error); - -const char *get_header_value(const Headers &headers, const std::string &key, - size_t id = 0, const char *def = nullptr); - -std::string params_to_query_str(const Params ¶ms); - -void parse_query_text(const std::string &s, Params ¶ms); - -bool parse_multipart_boundary(const std::string &content_type, - std::string &boundary); - -bool parse_range_header(const std::string &s, Ranges &ranges); - -int close_socket(socket_t sock); - -ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags); - -ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags); - -enum class EncodingType { None = 0, Gzip, Brotli }; - -EncodingType encoding_type(const Request &req, const Response &res); - -class BufferStream : public Stream { -public: - BufferStream() = default; - ~BufferStream() override = default; - - bool is_readable() const override; - bool is_writable() const override; - ssize_t read(char *ptr, size_t size) override; - ssize_t write(const char *ptr, size_t size) override; - void get_remote_ip_and_port(std::string &ip, int &port) const override; - void get_local_ip_and_port(std::string &ip, int &port) const override; - socket_t socket() const override; - - const std::string &get_buffer() const; - -private: - std::string buffer; - size_t position = 0; -}; - -class compressor { -public: - virtual ~compressor() = default; - - typedef std::function Callback; - virtual bool compress(const char *data, size_t data_length, bool last, - Callback callback) = 0; -}; - -class decompressor { -public: - virtual ~decompressor() = default; - - virtual bool is_valid() const = 0; - - typedef std::function Callback; - virtual bool decompress(const char *data, size_t data_length, - Callback callback) = 0; -}; - -class nocompressor : public compressor { -public: - ~nocompressor() override = default; - - bool compress(const char *data, size_t data_length, bool /*last*/, - Callback callback) override; -}; - -#ifdef CPPHTTPLIB_ZLIB_SUPPORT -class gzip_compressor : public compressor { -public: - gzip_compressor(); - ~gzip_compressor() override; - - bool compress(const char *data, size_t data_length, bool last, - Callback callback) override; - -private: - bool is_valid_ = false; - z_stream strm_; -}; - -class gzip_decompressor : public decompressor { -public: - gzip_decompressor(); - ~gzip_decompressor() override; - - bool is_valid() const override; - - bool decompress(const char *data, size_t data_length, - Callback callback) override; - -private: - bool is_valid_ = false; - z_stream strm_; -}; -#endif - -#ifdef CPPHTTPLIB_BROTLI_SUPPORT -class brotli_compressor : public compressor { -public: - brotli_compressor(); - ~brotli_compressor(); - - bool compress(const char *data, size_t data_length, bool last, - Callback callback) override; - -private: - BrotliEncoderState *state_ = nullptr; -}; - -class brotli_decompressor : public decompressor { -public: - brotli_decompressor(); - ~brotli_decompressor(); - - bool is_valid() const override; - - bool decompress(const char *data, size_t data_length, - Callback callback) override; - -private: - BrotliDecoderResult decoder_r; - BrotliDecoderState *decoder_s = nullptr; -}; -#endif - -// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` -// to store data. The call can set memory on stack for performance. -class stream_line_reader { -public: - stream_line_reader(Stream &strm, char *fixed_buffer, - size_t fixed_buffer_size); - const char *ptr() const; - size_t size() const; - bool end_with_crlf() const; - bool getline(); - -private: - void append(char c); - - Stream &strm_; - char *fixed_buffer_; - const size_t fixed_buffer_size_; - size_t fixed_buffer_used_size_ = 0; - std::string glowable_buffer_; -}; - -class mmap { -public: - mmap(const char *path); - ~mmap(); - - bool open(const char *path); - void close(); - - bool is_open() const; - size_t size() const; - const char *data() const; - -private: -#if defined(_WIN32) - HANDLE hFile_; - HANDLE hMapping_; -#else - int fd_; -#endif - size_t size_; - void *addr_; -}; - -} // namespace detail - -// ---------------------------------------------------------------------------- - -/* - * Implementation that will be part of the .cc file if split into .h + .cc. - */ - namespace detail { inline bool is_hex(char c, int &v) { @@ -2308,7 +477,7 @@ inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, val = 0; for (; cnt; i++, cnt--) { if (!s[i]) { return false; } - auto v = 0; + int v = 0; if (is_hex(s[i], v)) { val = val * 16 + v; } else { @@ -2318,8 +487,8 @@ inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, return true; } -inline std::string from_i_to_hex(size_t n) { - static const auto charset = "0123456789abcdef"; +inline std::string from_i_to_hex(uint64_t n) { + const char *charset = "0123456789abcdef"; std::string ret; do { ret = charset[n & 15] + ret; @@ -2330,29 +499,29 @@ inline std::string from_i_to_hex(size_t n) { inline size_t to_utf8(int code, char *buff) { if (code < 0x0080) { - buff[0] = static_cast(code & 0x7F); + buff[0] = (code & 0x7F); return 1; } else if (code < 0x0800) { - buff[0] = static_cast(0xC0 | ((code >> 6) & 0x1F)); - buff[1] = static_cast(0x80 | (code & 0x3F)); + buff[0] = (0xC0 | ((code >> 6) & 0x1F)); + buff[1] = (0x80 | (code & 0x3F)); return 2; } else if (code < 0xD800) { - buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); - buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); - buff[2] = static_cast(0x80 | (code & 0x3F)); + buff[0] = (0xE0 | ((code >> 12) & 0xF)); + buff[1] = (0x80 | ((code >> 6) & 0x3F)); + buff[2] = (0x80 | (code & 0x3F)); return 3; } else if (code < 0xE000) { // D800 - DFFF is invalid... return 0; } else if (code < 0x10000) { - buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); - buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); - buff[2] = static_cast(0x80 | (code & 0x3F)); + buff[0] = (0xE0 | ((code >> 12) & 0xF)); + buff[1] = (0x80 | ((code >> 6) & 0x3F)); + buff[2] = (0x80 | (code & 0x3F)); return 3; } else if (code < 0x110000) { - buff[0] = static_cast(0xF0 | ((code >> 18) & 0x7)); - buff[1] = static_cast(0x80 | ((code >> 12) & 0x3F)); - buff[2] = static_cast(0x80 | ((code >> 6) & 0x3F)); - buff[3] = static_cast(0x80 | (code & 0x3F)); + buff[0] = (0xF0 | ((code >> 18) & 0x7)); + buff[1] = (0x80 | ((code >> 12) & 0x3F)); + buff[2] = (0x80 | ((code >> 6) & 0x3F)); + buff[3] = (0x80 | (code & 0x3F)); return 4; } @@ -2369,11 +538,11 @@ inline std::string base64_encode(const std::string &in) { std::string out; out.reserve(in.size()); - auto val = 0; - auto valb = -6; + int val = 0; + int valb = -6; - for (auto c : in) { - val = (val << 8) + static_cast(c); + for (uint8_t c : in) { + val = (val << 8) + c; valb += 8; while (valb >= 0) { out.push_back(lookup[(val >> valb) & 0x3F]); @@ -2381,7 +550,9 @@ inline std::string base64_encode(const std::string &in) { } } - if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); } + if (valb > -6) { + out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); + } while (out.size() % 4) { out.push_back('='); @@ -2391,12 +562,8 @@ inline std::string base64_encode(const std::string &in) { } inline bool is_file(const std::string &path) { -#ifdef _WIN32 - return _access_s(path.c_str(), 0) == 0; -#else struct stat st; return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode); -#endif } inline bool is_dir(const std::string &path) { @@ -2417,11 +584,6 @@ inline bool is_valid_path(const std::string &path) { // Read component auto beg = i; while (i < path.size() && path[i] != '/') { - if (path[i] == '\0') { - return false; - } else if (path[i] == '\\') { - return false; - } i++; } @@ -2446,320 +608,108 @@ inline bool is_valid_path(const std::string &path) { return true; } -inline std::string encode_query_param(const std::string &value) { - std::ostringstream escaped; - escaped.fill('0'); - escaped << std::hex; - - for (auto c : value) { - if (std::isalnum(static_cast(c)) || c == '-' || c == '_' || - c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' || - c == ')') { - escaped << c; - } else { - escaped << std::uppercase; - escaped << '%' << std::setw(2) - << static_cast(static_cast(c)); - escaped << std::nouppercase; - } - } - - return escaped.str(); -} - -inline std::string encode_url(const std::string &s) { - std::string result; - result.reserve(s.size()); - - for (size_t i = 0; s[i]; i++) { - switch (s[i]) { - case ' ': result += "%20"; break; - case '+': result += "%2B"; break; - case '\r': result += "%0D"; break; - case '\n': result += "%0A"; break; - case '\'': result += "%27"; break; - case ',': result += "%2C"; break; - // case ':': result += "%3A"; break; // ok? probably... - case ';': result += "%3B"; break; - default: - auto c = static_cast(s[i]); - if (c >= 0x80) { - result += '%'; - char hex[4]; - auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c); - assert(len == 2); - result.append(hex, static_cast(len)); - } else { - result += s[i]; - } - break; - } - } - - return result; -} - -inline std::string decode_url(const std::string &s, - bool convert_plus_to_space) { - std::string result; - - for (size_t i = 0; i < s.size(); i++) { - if (s[i] == '%' && i + 1 < s.size()) { - if (s[i + 1] == 'u') { - auto val = 0; - if (from_hex_to_i(s, i + 2, 4, val)) { - // 4 digits Unicode codes - char buff[4]; - size_t len = to_utf8(val, buff); - if (len > 0) { result.append(buff, len); } - i += 5; // 'u0000' - } else { - result += s[i]; - } - } else { - auto val = 0; - if (from_hex_to_i(s, i + 1, 2, val)) { - // 2 digits hex codes - result += static_cast(val); - i += 2; // '00' - } else { - result += s[i]; - } - } - } else if (convert_plus_to_space && s[i] == '+') { - result += ' '; - } else { - result += s[i]; - } - } - - return result; -} - inline void read_file(const std::string &path, std::string &out) { std::ifstream fs(path, std::ios_base::binary); fs.seekg(0, std::ios_base::end); auto size = fs.tellg(); fs.seekg(0); out.resize(static_cast(size)); - fs.read(&out[0], static_cast(size)); + fs.read(&out[0], size); } inline std::string file_extension(const std::string &path) { std::smatch m; - static auto re = std::regex("\\.([a-zA-Z0-9]+)$"); - if (std::regex_search(path, m, re)) { return m[1].str(); } + auto pat = std::regex("\\.([a-zA-Z0-9]+)$"); + if (std::regex_search(path, m, pat)) { return m[1].str(); } return std::string(); } -inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; } +template void split(const char *b, const char *e, char d, Fn fn) { + int i = 0; + int beg = 0; -inline std::pair trim(const char *b, const char *e, size_t left, - size_t right) { - while (b + left < e && is_space_or_tab(b[left])) { - left++; - } - while (right > 0 && is_space_or_tab(b[right - 1])) { - right--; - } - return std::make_pair(left, right); -} - -inline std::string trim_copy(const std::string &s) { - auto r = trim(s.data(), s.data() + s.size(), 0, s.size()); - return s.substr(r.first, r.second - r.first); -} - -inline std::string trim_double_quotes_copy(const std::string &s) { - if (s.length() >= 2 && s.front() == '"' && s.back() == '"') { - return s.substr(1, s.size() - 2); - } - return s; -} - -inline void split(const char *b, const char *e, char d, - std::function fn) { - return split(b, e, d, (std::numeric_limits::max)(), std::move(fn)); -} - -inline void split(const char *b, const char *e, char d, size_t m, - std::function fn) { - size_t i = 0; - size_t beg = 0; - size_t count = 1; - - while (e ? (b + i < e) : (b[i] != '\0')) { - if (b[i] == d && count < m) { - auto r = trim(b, e, beg, i); - if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } + while (e ? (b + i != e) : (b[i] != '\0')) { + if (b[i] == d) { + fn(&b[beg], &b[i]); beg = i + 1; - count++; } i++; } - if (i) { - auto r = trim(b, e, beg, i); - if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } - } + if (i) { fn(&b[beg], &b[i]); } } -inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer, - size_t fixed_buffer_size) - : strm_(strm), fixed_buffer_(fixed_buffer), - fixed_buffer_size_(fixed_buffer_size) {} +// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` +// to store data. The call can set memory on stack for performance. +class stream_line_reader { +public: + stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size) + : strm_(strm), fixed_buffer_(fixed_buffer), + fixed_buffer_size_(fixed_buffer_size) {} -inline const char *stream_line_reader::ptr() const { - if (glowable_buffer_.empty()) { - return fixed_buffer_; - } else { - return glowable_buffer_.data(); - } -} - -inline size_t stream_line_reader::size() const { - if (glowable_buffer_.empty()) { - return fixed_buffer_used_size_; - } else { - return glowable_buffer_.size(); - } -} - -inline bool stream_line_reader::end_with_crlf() const { - auto end = ptr() + size(); - return size() >= 2 && end[-2] == '\r' && end[-1] == '\n'; -} - -inline bool stream_line_reader::getline() { - fixed_buffer_used_size_ = 0; - glowable_buffer_.clear(); - - for (size_t i = 0;; i++) { - char byte; - auto n = strm_.read(&byte, 1); - - if (n < 0) { - return false; - } else if (n == 0) { - if (i == 0) { - return false; - } else { - break; - } - } - - append(byte); - - if (byte == '\n') { break; } - } - - return true; -} - -inline void stream_line_reader::append(char c) { - if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { - fixed_buffer_[fixed_buffer_used_size_++] = c; - fixed_buffer_[fixed_buffer_used_size_] = '\0'; - } else { + const char *ptr() const { if (glowable_buffer_.empty()) { - assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); - glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); + return fixed_buffer_; + } else { + return glowable_buffer_.data(); } - glowable_buffer_ += c; - } -} - -inline mmap::mmap(const char *path) -#if defined(_WIN32) - : hFile_(NULL), hMapping_(NULL) -#else - : fd_(-1) -#endif - , - size_(0), addr_(nullptr) { - open(path); -} - -inline mmap::~mmap() { close(); } - -inline bool mmap::open(const char *path) { - close(); - -#if defined(_WIN32) - hFile_ = ::CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - - if (hFile_ == INVALID_HANDLE_VALUE) { return false; } - - size_ = ::GetFileSize(hFile_, NULL); - - hMapping_ = ::CreateFileMapping(hFile_, NULL, PAGE_READONLY, 0, 0, NULL); - - if (hMapping_ == NULL) { - close(); - return false; } - addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0); -#else - fd_ = ::open(path, O_RDONLY); - if (fd_ == -1) { return false; } - - struct stat sb; - if (fstat(fd_, &sb) == -1) { - close(); - return false; - } - size_ = static_cast(sb.st_size); - - addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0); -#endif - - if (addr_ == nullptr) { - close(); - return false; + size_t size() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_used_size_; + } else { + return glowable_buffer_.size(); + } } - return true; -} + bool getline() { + fixed_buffer_used_size_ = 0; + glowable_buffer_.clear(); -inline bool mmap::is_open() const { return addr_ != nullptr; } + for (size_t i = 0;; i++) { + char byte; + auto n = strm_.read(&byte, 1); -inline size_t mmap::size() const { return size_; } + if (n < 0) { + return false; + } else if (n == 0) { + if (i == 0) { + return false; + } else { + break; + } + } -inline const char *mmap::data() const { - return static_cast(addr_); -} + append(byte); -inline void mmap::close() { -#if defined(_WIN32) - if (addr_) { - ::UnmapViewOfFile(addr_); - addr_ = nullptr; + if (byte == '\n') { break; } + } + + return true; } - if (hMapping_) { - ::CloseHandle(hMapping_); - hMapping_ = NULL; +private: + void append(char c) { + if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { + fixed_buffer_[fixed_buffer_used_size_++] = c; + fixed_buffer_[fixed_buffer_used_size_] = '\0'; + } else { + if (glowable_buffer_.empty()) { + assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); + glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); + } + glowable_buffer_ += c; + } } - if (hFile_ != INVALID_HANDLE_VALUE) { - ::CloseHandle(hFile_); - hFile_ = INVALID_HANDLE_VALUE; - } -#else - if (addr_ != nullptr) { - munmap(addr_, size_); - addr_ = nullptr; - } + Stream &strm_; + char *fixed_buffer_; + const size_t fixed_buffer_size_; + size_t fixed_buffer_used_size_; + std::string glowable_buffer_; +}; - if (fd_ != -1) { - ::close(fd_); - fd_ = -1; - } -#endif - size_ = 0; -} inline int close_socket(socket_t sock) { #ifdef _WIN32 return closesocket(sock); @@ -2768,125 +718,19 @@ inline int close_socket(socket_t sock) { #endif } -template inline ssize_t handle_EINTR(T fn) { - ssize_t res = 0; - while (true) { - res = fn(); - if (res < 0 && errno == EINTR) { continue; } - break; - } - return res; -} - -inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) { - return handle_EINTR([&]() { - return recv(sock, -#ifdef _WIN32 - static_cast(ptr), static_cast(size), -#else - ptr, size, -#endif - flags); - }); -} - -inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size, - int flags) { - return handle_EINTR([&]() { - return send(sock, -#ifdef _WIN32 - static_cast(ptr), static_cast(size), -#else - ptr, size, -#endif - flags); - }); -} - -inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) { -#ifdef CPPHTTPLIB_USE_POLL - struct pollfd pfd_read; - pfd_read.fd = sock; - pfd_read.events = POLLIN; - - auto timeout = static_cast(sec * 1000 + usec / 1000); - - return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); -#else -#ifndef _WIN32 - if (sock >= FD_SETSIZE) { return -1; } -#endif - +inline int select_read(socket_t sock, time_t sec, time_t usec) { fd_set fds; FD_ZERO(&fds); FD_SET(sock, &fds); timeval tv; tv.tv_sec = static_cast(sec); - tv.tv_usec = static_cast(usec); + tv.tv_usec = static_cast(usec); - return handle_EINTR([&]() { - return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); - }); -#endif + return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); } -inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) { -#ifdef CPPHTTPLIB_USE_POLL - struct pollfd pfd_read; - pfd_read.fd = sock; - pfd_read.events = POLLOUT; - - auto timeout = static_cast(sec * 1000 + usec / 1000); - - return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); -#else -#ifndef _WIN32 - if (sock >= FD_SETSIZE) { return -1; } -#endif - - fd_set fds; - FD_ZERO(&fds); - FD_SET(sock, &fds); - - timeval tv; - tv.tv_sec = static_cast(sec); - tv.tv_usec = static_cast(usec); - - return handle_EINTR([&]() { - return select(static_cast(sock + 1), nullptr, &fds, nullptr, &tv); - }); -#endif -} - -inline Error wait_until_socket_is_ready(socket_t sock, time_t sec, - time_t usec) { -#ifdef CPPHTTPLIB_USE_POLL - struct pollfd pfd_read; - pfd_read.fd = sock; - pfd_read.events = POLLIN | POLLOUT; - - auto timeout = static_cast(sec * 1000 + usec / 1000); - - auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); - - if (poll_res == 0) { return Error::ConnectionTimeout; } - - if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) { - auto error = 0; - socklen_t len = sizeof(error); - auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, - reinterpret_cast(&error), &len); - auto successful = res >= 0 && !error; - return successful ? Error::Success : Error::Connection; - } - - return Error::Connection; -#else -#ifndef _WIN32 - if (sock >= FD_SETSIZE) { return Error::Connection; } -#endif - +inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { fd_set fdsr; FD_ZERO(&fdsr); FD_SET(sock, &fdsr); @@ -2896,155 +740,53 @@ inline Error wait_until_socket_is_ready(socket_t sock, time_t sec, timeval tv; tv.tv_sec = static_cast(sec); - tv.tv_usec = static_cast(usec); + tv.tv_usec = static_cast(usec); - auto ret = handle_EINTR([&]() { - return select(static_cast(sock + 1), &fdsr, &fdsw, &fdse, &tv); - }); - - if (ret == 0) { return Error::ConnectionTimeout; } - - if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) { - auto error = 0; + if (select(static_cast(sock + 1), &fdsr, &fdsw, &fdse, &tv) < 0) { + return false; + } else if (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw)) { + int error = 0; socklen_t len = sizeof(error); - auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, - reinterpret_cast(&error), &len); - auto successful = res >= 0 && !error; - return successful ? Error::Success : Error::Connection; - } - return Error::Connection; -#endif -} - -inline bool is_socket_alive(socket_t sock) { - const auto val = detail::select_read(sock, 0, 0); - if (val == 0) { - return true; - } else if (val < 0 && errno == EBADF) { + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, &len) < 0 || + error) { + return false; + } + } else { return false; } - char buf[1]; - return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0; + + return true; } -class SocketStream : public Stream { -public: - SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, - time_t write_timeout_sec, time_t write_timeout_usec); - ~SocketStream() override; +template +inline bool read_and_close_socket(socket_t sock, size_t keep_alive_max_count, + T callback) { + bool ret = false; - bool is_readable() const override; - bool is_writable() const override; - ssize_t read(char *ptr, size_t size) override; - ssize_t write(const char *ptr, size_t size) override; - void get_remote_ip_and_port(std::string &ip, int &port) const override; - void get_local_ip_and_port(std::string &ip, int &port) const override; - socket_t socket() const override; + if (keep_alive_max_count > 0) { + auto count = keep_alive_max_count; + while (count > 0 && + detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { + SocketStream strm(sock); + auto last_connection = count == 1; + auto connection_close = false; -private: - socket_t sock_; - time_t read_timeout_sec_; - time_t read_timeout_usec_; - time_t write_timeout_sec_; - time_t write_timeout_usec_; + ret = callback(strm, last_connection, connection_close); + if (!ret || connection_close) { break; } - std::vector read_buff_; - size_t read_buff_off_ = 0; - size_t read_buff_content_size_ = 0; - - static const size_t read_buff_size_ = 1024l * 4; -}; - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -class SSLSocketStream : public Stream { -public: - SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, - time_t read_timeout_usec, time_t write_timeout_sec, - time_t write_timeout_usec); - ~SSLSocketStream() override; - - bool is_readable() const override; - bool is_writable() const override; - ssize_t read(char *ptr, size_t size) override; - ssize_t write(const char *ptr, size_t size) override; - void get_remote_ip_and_port(std::string &ip, int &port) const override; - void get_local_ip_and_port(std::string &ip, int &port) const override; - socket_t socket() const override; - -private: - socket_t sock_; - SSL *ssl_; - time_t read_timeout_sec_; - time_t read_timeout_usec_; - time_t write_timeout_sec_; - time_t write_timeout_usec_; -}; -#endif - -inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) { - using namespace std::chrono; - auto start = steady_clock::now(); - while (true) { - auto val = select_read(sock, 0, 10000); - if (val < 0) { - return false; - } else if (val == 0) { - auto current = steady_clock::now(); - auto duration = duration_cast(current - start); - auto timeout = keep_alive_timeout_sec * 1000; - if (duration.count() > timeout) { return false; } - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } else { - return true; + count--; } + } else { + SocketStream strm(sock); + auto dummy_connection_close = false; + ret = callback(strm, true, dummy_connection_close); } -} -template -inline bool -process_server_socket_core(const std::atomic &svr_sock, socket_t sock, - size_t keep_alive_max_count, - time_t keep_alive_timeout_sec, T callback) { - assert(keep_alive_max_count > 0); - auto ret = false; - auto count = keep_alive_max_count; - while (svr_sock != INVALID_SOCKET && count > 0 && - keep_alive(sock, keep_alive_timeout_sec)) { - auto close_connection = count == 1; - auto connection_closed = false; - ret = callback(close_connection, connection_closed); - if (!ret || connection_closed) { break; } - count--; - } + close_socket(sock); return ret; } -template -inline bool -process_server_socket(const std::atomic &svr_sock, socket_t sock, - size_t keep_alive_max_count, - time_t keep_alive_timeout_sec, time_t read_timeout_sec, - time_t read_timeout_usec, time_t write_timeout_sec, - time_t write_timeout_usec, T callback) { - return process_server_socket_core( - svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec, - [&](bool close_connection, bool &connection_closed) { - SocketStream strm(sock, read_timeout_sec, read_timeout_usec, - write_timeout_sec, write_timeout_usec); - return callback(strm, close_connection, connection_closed); - }); -} - -inline bool process_client_socket(socket_t sock, time_t read_timeout_sec, - time_t read_timeout_usec, - time_t write_timeout_sec, - time_t write_timeout_usec, - std::function callback) { - SocketStream strm(sock, read_timeout_sec, read_timeout_usec, - write_timeout_sec, write_timeout_usec); - return callback(strm); -} - inline int shutdown_socket(socket_t sock) { #ifdef _WIN32 return shutdown(sock, SD_BOTH); @@ -3053,128 +795,57 @@ inline int shutdown_socket(socket_t sock) { #endif } -template -socket_t create_socket(const std::string &host, const std::string &ip, int port, - int address_family, int socket_flags, bool tcp_nodelay, - SocketOptions socket_options, - BindOrConnect bind_or_connect) { +template +socket_t create_socket(const char *host, int port, Fn fn, + int socket_flags = 0) { +#ifdef _WIN32 +#define SO_SYNCHRONOUS_NONALERT 0x20 +#define SO_OPENTYPE 0x7008 + + int opt = SO_SYNCHRONOUS_NONALERT; + setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&opt, + sizeof(opt)); +#endif + // Get address info - const char *node = nullptr; struct addrinfo hints; struct addrinfo *result; memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = socket_flags; hints.ai_protocol = 0; - if (!ip.empty()) { - node = ip.c_str(); - // Ask getaddrinfo to convert IP in c-string to address - hints.ai_family = AF_UNSPEC; - hints.ai_flags = AI_NUMERICHOST; - } else { - if (!host.empty()) { node = host.c_str(); } - hints.ai_family = address_family; - hints.ai_flags = socket_flags; - } - -#ifndef _WIN32 - if (hints.ai_family == AF_UNIX) { - const auto addrlen = host.length(); - if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; } - - auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol); - if (sock != INVALID_SOCKET) { - sockaddr_un addr{}; - addr.sun_family = AF_UNIX; - std::copy(host.begin(), host.end(), addr.sun_path); - - hints.ai_addr = reinterpret_cast(&addr); - hints.ai_addrlen = static_cast( - sizeof(addr) - sizeof(addr.sun_path) + addrlen); - - fcntl(sock, F_SETFD, FD_CLOEXEC); - if (socket_options) { socket_options(sock); } - - if (!bind_or_connect(sock, hints)) { - close_socket(sock); - sock = INVALID_SOCKET; - } - } - return sock; - } -#endif - auto service = std::to_string(port); - if (getaddrinfo(node, service.c_str(), &hints, &result)) { -#if defined __linux__ && !defined __ANDROID__ - res_init(); -#endif + if (getaddrinfo(host, service.c_str(), &hints, &result)) { return INVALID_SOCKET; } for (auto rp = result; rp; rp = rp->ai_next) { // Create a socket #ifdef _WIN32 - auto sock = - WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0, - WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED); - /** - * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1 - * and above the socket creation fails on older Windows Systems. - * - * Let's try to create a socket the old way in this case. - * - * Reference: - * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa - * - * WSA_FLAG_NO_HANDLE_INHERIT: - * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with - * SP1, and later - * - */ - if (sock == INVALID_SOCKET) { - sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - } + auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, + nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT); #else auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); #endif if (sock == INVALID_SOCKET) { continue; } #ifndef _WIN32 - if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { - close_socket(sock); - continue; - } + if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; } #endif - if (tcp_nodelay) { - auto yes = 1; -#ifdef _WIN32 - setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, - reinterpret_cast(&yes), sizeof(yes)); -#else - setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, - reinterpret_cast(&yes), sizeof(yes)); + // Make 'reuse address' option available + int yes = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)); +#ifdef SO_REUSEPORT + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char *)&yes, sizeof(yes)); #endif - } - - if (socket_options) { socket_options(sock); } - - if (rp->ai_family == AF_INET6) { - auto no = 0; -#ifdef _WIN32 - setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, - reinterpret_cast(&no), sizeof(no)); -#else - setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, - reinterpret_cast(&no), sizeof(no)); -#endif - } // bind or connect - if (bind_or_connect(sock, *rp)) { + if (fn(sock, *rp)) { freeaddrinfo(result); return sock; } @@ -3205,659 +876,229 @@ inline bool is_connection_error() { #endif } -inline bool bind_ip_address(socket_t sock, const std::string &host) { - struct addrinfo hints; - struct addrinfo *result; - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - - if (getaddrinfo(host.c_str(), "0", &hints, &result)) { return false; } - - auto ret = false; - for (auto rp = result; rp; rp = rp->ai_next) { - const auto &ai = *rp; - if (!::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { - ret = true; - break; - } - } - - freeaddrinfo(result); - return ret; -} - -#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__ -#define USE_IF2IP -#endif - -#ifdef USE_IF2IP -inline std::string if2ip(int address_family, const std::string &ifn) { - struct ifaddrs *ifap; - getifaddrs(&ifap); - std::string addr_candidate; - for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr && ifn == ifa->ifa_name && - (AF_UNSPEC == address_family || - ifa->ifa_addr->sa_family == address_family)) { - if (ifa->ifa_addr->sa_family == AF_INET) { - auto sa = reinterpret_cast(ifa->ifa_addr); - char buf[INET_ADDRSTRLEN]; - if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) { - freeifaddrs(ifap); - return std::string(buf, INET_ADDRSTRLEN); - } - } else if (ifa->ifa_addr->sa_family == AF_INET6) { - auto sa = reinterpret_cast(ifa->ifa_addr); - if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) { - char buf[INET6_ADDRSTRLEN] = {}; - if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) { - // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL - auto s6_addr_head = sa->sin6_addr.s6_addr[0]; - if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) { - addr_candidate = std::string(buf, INET6_ADDRSTRLEN); - } else { - freeifaddrs(ifap); - return std::string(buf, INET6_ADDRSTRLEN); - } - } - } - } - } - } - freeifaddrs(ifap); - return addr_candidate; -} -#endif - -inline socket_t create_client_socket( - const std::string &host, const std::string &ip, int port, - int address_family, bool tcp_nodelay, SocketOptions socket_options, - time_t connection_timeout_sec, time_t connection_timeout_usec, - time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, - time_t write_timeout_usec, const std::string &intf, Error &error) { - auto sock = create_socket( - host, ip, port, address_family, 0, tcp_nodelay, std::move(socket_options), - [&](socket_t sock2, struct addrinfo &ai) -> bool { - if (!intf.empty()) { -#ifdef USE_IF2IP - auto ip_from_if = if2ip(address_family, intf); - if (ip_from_if.empty()) { ip_from_if = intf; } - if (!bind_ip_address(sock2, ip_from_if)) { - error = Error::BindIPAddress; - return false; - } -#endif - } - - set_nonblocking(sock2, true); - - auto ret = - ::connect(sock2, ai.ai_addr, static_cast(ai.ai_addrlen)); - - if (ret < 0) { - if (is_connection_error()) { - error = Error::Connection; - return false; - } - error = wait_until_socket_is_ready(sock2, connection_timeout_sec, - connection_timeout_usec); - if (error != Error::Success) { return false; } - } - - set_nonblocking(sock2, false); - - { -#ifdef _WIN32 - auto timeout = static_cast(read_timeout_sec * 1000 + - read_timeout_usec / 1000); - setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, - reinterpret_cast(&timeout), sizeof(timeout)); -#else - timeval tv; - tv.tv_sec = static_cast(read_timeout_sec); - tv.tv_usec = static_cast(read_timeout_usec); - setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, - reinterpret_cast(&tv), sizeof(tv)); -#endif - } - { - -#ifdef _WIN32 - auto timeout = static_cast(write_timeout_sec * 1000 + - write_timeout_usec / 1000); - setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, - reinterpret_cast(&timeout), sizeof(timeout)); -#else - timeval tv; - tv.tv_sec = static_cast(write_timeout_sec); - tv.tv_usec = static_cast(write_timeout_usec); - setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, - reinterpret_cast(&tv), sizeof(tv)); -#endif - } - - error = Error::Success; - return true; - }); - - if (sock != INVALID_SOCKET) { - error = Error::Success; - } else { - if (error == Error::Success) { error = Error::Connection; } - } - - return sock; -} - -inline bool get_ip_and_port(const struct sockaddr_storage &addr, - socklen_t addr_len, std::string &ip, int &port) { - if (addr.ss_family == AF_INET) { - port = ntohs(reinterpret_cast(&addr)->sin_port); - } else if (addr.ss_family == AF_INET6) { - port = - ntohs(reinterpret_cast(&addr)->sin6_port); - } else { - return false; - } - - std::array ipstr{}; - if (getnameinfo(reinterpret_cast(&addr), addr_len, - ipstr.data(), static_cast(ipstr.size()), nullptr, - 0, NI_NUMERICHOST)) { - return false; - } - - ip = ipstr.data(); - return true; -} - -inline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) { +inline std::string get_remote_addr(socket_t sock) { struct sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); - if (!getsockname(sock, reinterpret_cast(&addr), - &addr_len)) { - get_ip_and_port(addr, addr_len, ip, port); - } -} + socklen_t len = sizeof(addr); -inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) { - struct sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); + if (!getpeername(sock, (struct sockaddr *)&addr, &len)) { + char ipstr[NI_MAXHOST]; - if (!getpeername(sock, reinterpret_cast(&addr), - &addr_len)) { -#ifndef _WIN32 - if (addr.ss_family == AF_UNIX) { -#if defined(__linux__) - struct ucred ucred; - socklen_t len = sizeof(ucred); - if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) { - port = ucred.pid; - } -#elif defined(SOL_LOCAL) && defined(SO_PEERPID) // __APPLE__ - pid_t pid; - socklen_t len = sizeof(pid); - if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) { - port = pid; - } -#endif - return; + if (!getnameinfo((struct sockaddr *)&addr, len, ipstr, sizeof(ipstr), + nullptr, 0, NI_NUMERICHOST)) { + return ipstr; } -#endif - get_ip_and_port(addr, addr_len, ip, port); } + + return std::string(); } -inline constexpr unsigned int str2tag_core(const char *s, size_t l, - unsigned int h) { - return (l == 0) - ? h - : str2tag_core( - s + 1, l - 1, - // Unsets the 6 high bits of h, therefore no overflow happens - (((std::numeric_limits::max)() >> 6) & - h * 33) ^ - static_cast(*s)); -} - -inline unsigned int str2tag(const std::string &s) { - return str2tag_core(s.data(), s.size(), 0); -} - -namespace udl { - -inline constexpr unsigned int operator"" _t(const char *s, size_t l) { - return str2tag_core(s, l, 0); -} - -} // namespace udl - -inline std::string -find_content_type(const std::string &path, - const std::map &user_data, - const std::string &default_content_type) { +inline const char *find_content_type(const std::string &path) { auto ext = file_extension(path); - - auto it = user_data.find(ext); - if (it != user_data.end()) { return it->second; } - - using udl::operator""_t; - - switch (str2tag(ext)) { - default: return default_content_type; - - case "css"_t: return "text/css"; - case "csv"_t: return "text/csv"; - case "htm"_t: - case "html"_t: return "text/html"; - case "js"_t: - case "mjs"_t: return "text/javascript"; - case "txt"_t: return "text/plain"; - case "vtt"_t: return "text/vtt"; - - case "apng"_t: return "image/apng"; - case "avif"_t: return "image/avif"; - case "bmp"_t: return "image/bmp"; - case "gif"_t: return "image/gif"; - case "png"_t: return "image/png"; - case "svg"_t: return "image/svg+xml"; - case "webp"_t: return "image/webp"; - case "ico"_t: return "image/x-icon"; - case "tif"_t: return "image/tiff"; - case "tiff"_t: return "image/tiff"; - case "jpg"_t: - case "jpeg"_t: return "image/jpeg"; - - case "mp4"_t: return "video/mp4"; - case "mpeg"_t: return "video/mpeg"; - case "webm"_t: return "video/webm"; - - case "mp3"_t: return "audio/mp3"; - case "mpga"_t: return "audio/mpeg"; - case "weba"_t: return "audio/webm"; - case "wav"_t: return "audio/wave"; - - case "otf"_t: return "font/otf"; - case "ttf"_t: return "font/ttf"; - case "woff"_t: return "font/woff"; - case "woff2"_t: return "font/woff2"; - - case "7z"_t: return "application/x-7z-compressed"; - case "atom"_t: return "application/atom+xml"; - case "pdf"_t: return "application/pdf"; - case "json"_t: return "application/json"; - case "rss"_t: return "application/rss+xml"; - case "tar"_t: return "application/x-tar"; - case "xht"_t: - case "xhtml"_t: return "application/xhtml+xml"; - case "xslt"_t: return "application/xslt+xml"; - case "xml"_t: return "application/xml"; - case "gz"_t: return "application/gzip"; - case "zip"_t: return "application/zip"; - case "wasm"_t: return "application/wasm"; + if (ext == "txt") { + return "text/plain"; + } else if (ext == "html") { + return "text/html"; + } else if (ext == "css") { + return "text/css"; + } else if (ext == "jpeg" || ext == "jpg") { + return "image/jpg"; + } else if (ext == "png") { + return "image/png"; + } else if (ext == "gif") { + return "image/gif"; + } else if (ext == "svg") { + return "image/svg+xml"; + } else if (ext == "ico") { + return "image/x-icon"; + } else if (ext == "json") { + return "application/json"; + } else if (ext == "pdf") { + return "application/pdf"; + } else if (ext == "js") { + return "application/javascript"; + } else if (ext == "xml") { + return "application/xml"; + } else if (ext == "xhtml") { + return "application/xhtml+xml"; } + return nullptr; } -inline bool can_compress_content_type(const std::string &content_type) { - using udl::operator""_t; - - auto tag = str2tag(content_type); - - switch (tag) { - case "image/svg+xml"_t: - case "application/javascript"_t: - case "application/json"_t: - case "application/xml"_t: - case "application/protobuf"_t: - case "application/xhtml+xml"_t: return true; - +inline const char *status_message(int status) { + switch (status) { + case 200: return "OK"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 400: return "Bad Request"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 413: return "Payload Too Large"; + case 414: return "Request-URI Too Long"; + case 415: return "Unsupported Media Type"; default: - return !content_type.rfind("text/", 0) && tag != "text/event-stream"_t; + case 500: return "Internal Server Error"; } } -inline EncodingType encoding_type(const Request &req, const Response &res) { - auto ret = - detail::can_compress_content_type(res.get_header_value("Content-Type")); - if (!ret) { return EncodingType::None; } - - const auto &s = req.get_header_value("Accept-Encoding"); - (void)(s); - -#ifdef CPPHTTPLIB_BROTLI_SUPPORT - // TODO: 'Accept-Encoding' has br, not br;q=0 - ret = s.find("br") != std::string::npos; - if (ret) { return EncodingType::Brotli; } -#endif - #ifdef CPPHTTPLIB_ZLIB_SUPPORT - // TODO: 'Accept-Encoding' has gzip, not gzip;q=0 - ret = s.find("gzip") != std::string::npos; - if (ret) { return EncodingType::Gzip; } -#endif - - return EncodingType::None; +inline bool can_compress(const std::string &content_type) { + return !content_type.find("text/") || content_type == "image/svg+xml" || + content_type == "application/javascript" || + content_type == "application/json" || + content_type == "application/xml" || + content_type == "application/xhtml+xml"; } -inline bool nocompressor::compress(const char *data, size_t data_length, - bool /*last*/, Callback callback) { - if (!data_length) { return true; } - return callback(data, data_length); -} +inline bool compress(std::string &content) { + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; -#ifdef CPPHTTPLIB_ZLIB_SUPPORT -inline gzip_compressor::gzip_compressor() { - std::memset(&strm_, 0, sizeof(strm_)); - strm_.zalloc = Z_NULL; - strm_.zfree = Z_NULL; - strm_.opaque = Z_NULL; + auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, + Z_DEFAULT_STRATEGY); + if (ret != Z_OK) { return false; } - is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, - Z_DEFAULT_STRATEGY) == Z_OK; -} + strm.avail_in = content.size(); + strm.next_in = (Bytef *)content.data(); -inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); } - -inline bool gzip_compressor::compress(const char *data, size_t data_length, - bool last, Callback callback) { - assert(is_valid_); + std::string compressed; + const auto bufsiz = 16384; + char buff[bufsiz]; do { - constexpr size_t max_avail_in = - (std::numeric_limits::max)(); + strm.avail_out = bufsiz; + strm.next_out = (Bytef *)buff; + ret = deflate(&strm, Z_FINISH); + assert(ret != Z_STREAM_ERROR); + compressed.append(buff, bufsiz - strm.avail_out); + } while (strm.avail_out == 0); - strm_.avail_in = static_cast( - (std::min)(data_length, max_avail_in)); - strm_.next_in = const_cast(reinterpret_cast(data)); + assert(ret == Z_STREAM_END); + assert(strm.avail_in == 0); - data_length -= strm_.avail_in; - data += strm_.avail_in; - - auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH; - auto ret = Z_OK; - - std::array buff{}; - do { - strm_.avail_out = static_cast(buff.size()); - strm_.next_out = reinterpret_cast(buff.data()); - - ret = deflate(&strm_, flush); - if (ret == Z_STREAM_ERROR) { return false; } - - if (!callback(buff.data(), buff.size() - strm_.avail_out)) { - return false; - } - } while (strm_.avail_out == 0); - - assert((flush == Z_FINISH && ret == Z_STREAM_END) || - (flush == Z_NO_FLUSH && ret == Z_OK)); - assert(strm_.avail_in == 0); - } while (data_length > 0); + content.swap(compressed); + deflateEnd(&strm); return true; } -inline gzip_decompressor::gzip_decompressor() { - std::memset(&strm_, 0, sizeof(strm_)); - strm_.zalloc = Z_NULL; - strm_.zfree = Z_NULL; - strm_.opaque = Z_NULL; +class decompressor { +public: + decompressor() { + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; - // 15 is the value of wbits, which should be at the maximum possible value - // to ensure that any gzip stream can be decoded. The offset of 32 specifies - // that the stream type should be automatically detected either gzip or - // deflate. - is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK; -} + // 15 is the value of wbits, which should be at the maximum possible value + // to ensure that any gzip stream can be decoded. The offset of 16 specifies + // that the stream to decompress will be formatted with a gzip wrapper. + is_valid_ = inflateInit2(&strm, 16 + 15) == Z_OK; + } -inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); } + ~decompressor() { inflateEnd(&strm); } -inline bool gzip_decompressor::is_valid() const { return is_valid_; } + bool is_valid() const { return is_valid_; } -inline bool gzip_decompressor::decompress(const char *data, size_t data_length, - Callback callback) { - assert(is_valid_); + template + bool decompress(const char *data, size_t data_len, T callback) { + int ret = Z_OK; + std::string decompressed; - auto ret = Z_OK; + // strm.avail_in = content.size(); + // strm.next_in = (Bytef *)content.data(); + strm.avail_in = data_len; + strm.next_in = (Bytef *)data; - do { - constexpr size_t max_avail_in = - (std::numeric_limits::max)(); - - strm_.avail_in = static_cast( - (std::min)(data_length, max_avail_in)); - strm_.next_in = const_cast(reinterpret_cast(data)); - - data_length -= strm_.avail_in; - data += strm_.avail_in; - - std::array buff{}; - while (strm_.avail_in > 0 && ret == Z_OK) { - strm_.avail_out = static_cast(buff.size()); - strm_.next_out = reinterpret_cast(buff.data()); - - ret = inflate(&strm_, Z_NO_FLUSH); + const auto bufsiz = 16384; + char buff[bufsiz]; + do { + strm.avail_out = bufsiz; + strm.next_out = (Bytef *)buff; + ret = inflate(&strm, Z_NO_FLUSH); assert(ret != Z_STREAM_ERROR); switch (ret) { case Z_NEED_DICT: case Z_DATA_ERROR: - case Z_MEM_ERROR: inflateEnd(&strm_); return false; + case Z_MEM_ERROR: inflateEnd(&strm); return false; } - if (!callback(buff.data(), buff.size() - strm_.avail_out)) { - return false; - } + decompressed.append(buff, bufsiz - strm.avail_out); + } while (strm.avail_out == 0); + + if (ret == Z_STREAM_END) { + callback(decompressed.data(), decompressed.size()); + return true; } - if (ret != Z_OK && ret != Z_STREAM_END) { return false; } + return false; + } - } while (data_length > 0); - - return true; -} +private: + bool is_valid_; + z_stream strm; +}; #endif -#ifdef CPPHTTPLIB_BROTLI_SUPPORT -inline brotli_compressor::brotli_compressor() { - state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); -} - -inline brotli_compressor::~brotli_compressor() { - BrotliEncoderDestroyInstance(state_); -} - -inline bool brotli_compressor::compress(const char *data, size_t data_length, - bool last, Callback callback) { - std::array buff{}; - - auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS; - auto available_in = data_length; - auto next_in = reinterpret_cast(data); - - for (;;) { - if (last) { - if (BrotliEncoderIsFinished(state_)) { break; } - } else { - if (!available_in) { break; } - } - - auto available_out = buff.size(); - auto next_out = buff.data(); - - if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in, - &available_out, &next_out, nullptr)) { - return false; - } - - auto output_bytes = buff.size() - available_out; - if (output_bytes) { - callback(reinterpret_cast(buff.data()), output_bytes); - } - } - - return true; -} - -inline brotli_decompressor::brotli_decompressor() { - decoder_s = BrotliDecoderCreateInstance(0, 0, 0); - decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT - : BROTLI_DECODER_RESULT_ERROR; -} - -inline brotli_decompressor::~brotli_decompressor() { - if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); } -} - -inline bool brotli_decompressor::is_valid() const { return decoder_s; } - -inline bool brotli_decompressor::decompress(const char *data, - size_t data_length, - Callback callback) { - if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS || - decoder_r == BROTLI_DECODER_RESULT_ERROR) { - return 0; - } - - auto next_in = reinterpret_cast(data); - size_t avail_in = data_length; - size_t total_out; - - decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; - - std::array buff{}; - while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { - char *next_out = buff.data(); - size_t avail_out = buff.size(); - - decoder_r = BrotliDecoderDecompressStream( - decoder_s, &avail_in, &next_in, &avail_out, - reinterpret_cast(&next_out), &total_out); - - if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; } - - if (!callback(buff.data(), buff.size() - avail_out)) { return false; } - } - - return decoder_r == BROTLI_DECODER_RESULT_SUCCESS || - decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; -} -#endif - -inline bool has_header(const Headers &headers, const std::string &key) { +inline bool has_header(const Headers &headers, const char *key) { return headers.find(key) != headers.end(); } -inline const char *get_header_value(const Headers &headers, - const std::string &key, size_t id, - const char *def) { - auto rng = headers.equal_range(key); - auto it = rng.first; - std::advance(it, static_cast(id)); - if (it != rng.second) { return it->second.c_str(); } +inline const char *get_header_value(const Headers &headers, const char *key, + size_t id = 0, const char *def = nullptr) { + auto it = headers.find(key); + std::advance(it, id); + if (it != headers.end()) { return it->second.c_str(); } return def; } -inline bool compare_case_ignore(const std::string &a, const std::string &b) { - if (a.size() != b.size()) { return false; } - for (size_t i = 0; i < b.size(); i++) { - if (::tolower(a[i]) != ::tolower(b[i])) { return false; } +inline uint64_t get_header_value_uint64(const Headers &headers, const char *key, + int def = 0) { + auto it = headers.find(key); + if (it != headers.end()) { + return std::strtoull(it->second.data(), nullptr, 10); } + return def; +} + +inline bool read_headers(Stream &strm, Headers &headers) { + static std::regex re(R"((.+?):\s*(.+?)\s*\r\n)"); + + const auto bufsiz = 2048; + char buf[bufsiz]; + + stream_line_reader reader(strm, buf, bufsiz); + + for (;;) { + if (!reader.getline()) { return false; } + if (!strcmp(reader.ptr(), "\r\n")) { break; } + std::cmatch m; + if (std::regex_match(reader.ptr(), m, re)) { + auto key = std::string(m[1]); + auto val = std::string(m[2]); + headers.emplace(key, val); + } + } + return true; } template -inline bool parse_header(const char *beg, const char *end, T fn) { - // Skip trailing spaces and tabs. - while (beg < end && is_space_or_tab(end[-1])) { - end--; - } - - auto p = beg; - while (p < end && *p != ':') { - p++; - } - - if (p == end) { return false; } - - auto key_end = p; - - if (*p++ != ':') { return false; } - - while (p < end && is_space_or_tab(*p)) { - p++; - } - - if (p < end) { - auto key_len = key_end - beg; - if (!key_len) { return false; } - - auto key = std::string(beg, key_end); - auto val = compare_case_ignore(key, "Location") - ? std::string(p, end) - : decode_url(std::string(p, end), false); - fn(std::move(key), std::move(val)); - return true; - } - - return false; -} - -inline bool read_headers(Stream &strm, Headers &headers) { - const auto bufsiz = 2048; - char buf[bufsiz]; - stream_line_reader line_reader(strm, buf, bufsiz); - - for (;;) { - if (!line_reader.getline()) { return false; } - - // Check if the line ends with CRLF. - auto line_terminator_len = 2; - if (line_reader.end_with_crlf()) { - // Blank line indicates end of headers. - if (line_reader.size() == 2) { break; } -#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR - } else { - // Blank line indicates end of headers. - if (line_reader.size() == 1) { break; } - line_terminator_len = 1; - } -#else - } else { - continue; // Skip invalid line. - } -#endif - - if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; } - - // Exclude line terminator - auto end = line_reader.ptr() + line_reader.size() - line_terminator_len; - - parse_header(line_reader.ptr(), end, - [&](std::string &&key, std::string &&val) { - headers.emplace(std::move(key), std::move(val)); - }); - } - - return true; -} - -inline bool read_content_with_length(Stream &strm, uint64_t len, - Progress progress, - ContentReceiverWithProgress out) { +inline bool read_content_with_length(Stream &strm, size_t len, + Progress progress, T callback) { char buf[CPPHTTPLIB_RECV_BUFSIZ]; - uint64_t r = 0; + size_t r = 0; while (r < len) { - auto read_len = static_cast(len - r); - auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + auto n = strm.read(buf, std::min((len - r), CPPHTTPLIB_RECV_BUFSIZ)); if (n <= 0) { return false; } - if (!out(buf, static_cast(n), r, len)) { return false; } - r += static_cast(n); + callback(buf, n); + + r += n; if (progress) { if (!progress(r, len)) { return false; } @@ -3867,2053 +1108,612 @@ inline bool read_content_with_length(Stream &strm, uint64_t len, return true; } -inline void skip_content_with_length(Stream &strm, uint64_t len) { +inline void skip_content_with_length(Stream &strm, size_t len) { char buf[CPPHTTPLIB_RECV_BUFSIZ]; - uint64_t r = 0; + size_t r = 0; while (r < len) { - auto read_len = static_cast(len - r); - auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + auto n = strm.read(buf, std::min((len - r), CPPHTTPLIB_RECV_BUFSIZ)); if (n <= 0) { return; } - r += static_cast(n); + r += n; } } -inline bool read_content_without_length(Stream &strm, - ContentReceiverWithProgress out) { +template +inline bool read_content_without_length(Stream &strm, T callback) { char buf[CPPHTTPLIB_RECV_BUFSIZ]; - uint64_t r = 0; for (;;) { auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); - if (n <= 0) { return true; } - - if (!out(buf, static_cast(n), r, 0)) { return false; } - r += static_cast(n); + if (n < 0) { + return false; + } else if (n == 0) { + return true; + } + callback(buf, n); } return true; } template -inline bool read_content_chunked(Stream &strm, T &x, - ContentReceiverWithProgress out) { +inline bool read_content_chunked(Stream &strm, T callback) { const auto bufsiz = 16; char buf[bufsiz]; - stream_line_reader line_reader(strm, buf, bufsiz); + stream_line_reader reader(strm, buf, bufsiz); - if (!line_reader.getline()) { return false; } + if (!reader.getline()) { return false; } - unsigned long chunk_len; - while (true) { - char *end_ptr; + auto chunk_len = std::stoi(reader.ptr(), 0, 16); - chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16); - - if (end_ptr == line_reader.ptr()) { return false; } - if (chunk_len == ULONG_MAX) { return false; } - - if (chunk_len == 0) { break; } - - if (!read_content_with_length(strm, chunk_len, nullptr, out)) { + while (chunk_len > 0) { + if (!read_content_with_length(strm, chunk_len, nullptr, callback)) { return false; } - if (!line_reader.getline()) { return false; } + if (!reader.getline()) { return false; } - if (strcmp(line_reader.ptr(), "\r\n") != 0) { return false; } + if (strcmp(reader.ptr(), "\r\n")) { break; } - if (!line_reader.getline()) { return false; } + if (!reader.getline()) { return false; } + + chunk_len = std::stoi(reader.ptr(), 0, 16); } - assert(chunk_len == 0); - - // Trailer - if (!line_reader.getline()) { return false; } - - while (strcmp(line_reader.ptr(), "\r\n") != 0) { - if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; } - - // Exclude line terminator - constexpr auto line_terminator_len = 2; - auto end = line_reader.ptr() + line_reader.size() - line_terminator_len; - - parse_header(line_reader.ptr(), end, - [&](std::string &&key, std::string &&val) { - x.headers.emplace(std::move(key), std::move(val)); - }); - - if (!line_reader.getline()) { return false; } + if (chunk_len == 0) { + // Reader terminator after chunks + if (!reader.getline() || strcmp(reader.ptr(), "\r\n")) return false; } return true; } inline bool is_chunked_transfer_encoding(const Headers &headers) { - return compare_case_ignore( - get_header_value(headers, "Transfer-Encoding", 0, ""), "chunked"); + return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""), + "chunked"); } template -bool prepare_content_receiver(T &x, int &status, - ContentReceiverWithProgress receiver, - bool decompress, U callback) { - if (decompress) { - std::string encoding = x.get_header_value("Content-Encoding"); - std::unique_ptr decompressor; +bool read_content(Stream &strm, T &x, uint64_t payload_max_length, int &status, + Progress progress, U callback) { + + ContentReceiver out = [&](const char *buf, size_t n) { callback(buf, n); }; - if (encoding == "gzip" || encoding == "deflate") { #ifdef CPPHTTPLIB_ZLIB_SUPPORT - decompressor = detail::make_unique(); + detail::decompressor decompressor; + + if (!decompressor.is_valid()) { + status = 500; + return false; + } + + if (x.get_header_value("Content-Encoding") == "gzip") { + out = [&](const char *buf, size_t n) { + decompressor.decompress( + buf, n, [&](const char *buf, size_t n) { callback(buf, n); }); + }; + } #else - status = StatusCode::UnsupportedMediaType_415; - return false; + if (x.get_header_value("Content-Encoding") == "gzip") { + status = 415; + return false; + } #endif - } else if (encoding.find("br") != std::string::npos) { -#ifdef CPPHTTPLIB_BROTLI_SUPPORT - decompressor = detail::make_unique(); -#else - status = StatusCode::UnsupportedMediaType_415; - return false; -#endif - } - if (decompressor) { - if (decompressor->is_valid()) { - ContentReceiverWithProgress out = [&](const char *buf, size_t n, - uint64_t off, uint64_t len) { - return decompressor->decompress(buf, n, - [&](const char *buf2, size_t n2) { - return receiver(buf2, n2, off, len); - }); - }; - return callback(std::move(out)); + auto ret = true; + auto exceed_payload_max_length = false; + + if (is_chunked_transfer_encoding(x.headers)) { + ret = read_content_chunked(strm, out); + } else if (!has_header(x.headers, "Content-Length")) { + ret = read_content_without_length(strm, out); + } else { + auto len = get_header_value_uint64(x.headers, "Content-Length", 0); + if (len > 0) { + if ((len > payload_max_length) || + // For 32-bit platform + (sizeof(size_t) < sizeof(uint64_t) && + len > std::numeric_limits::max())) { + exceed_payload_max_length = true; + skip_content_with_length(strm, len); + ret = false; } else { - status = StatusCode::InternalServerError_500; - return false; + ret = read_content_with_length(strm, len, progress, out); } } } - ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off, - uint64_t len) { - return receiver(buf, n, off, len); - }; - return callback(std::move(out)); -} + if (!ret) { status = exceed_payload_max_length ? 413 : 400; } -template -bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, - Progress progress, ContentReceiverWithProgress receiver, - bool decompress) { - return prepare_content_receiver( - x, status, std::move(receiver), decompress, - [&](const ContentReceiverWithProgress &out) { - auto ret = true; - auto exceed_payload_max_length = false; - - if (is_chunked_transfer_encoding(x.headers)) { - ret = read_content_chunked(strm, x, out); - } else if (!has_header(x.headers, "Content-Length")) { - ret = read_content_without_length(strm, out); - } else { - auto len = get_header_value_u64(x.headers, "Content-Length", 0, 0); - if (len > payload_max_length) { - exceed_payload_max_length = true; - skip_content_with_length(strm, len); - ret = false; - } else if (len > 0) { - ret = read_content_with_length(strm, len, std::move(progress), out); - } - } - - if (!ret) { - status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413 - : StatusCode::BadRequest_400; - } - return ret; - }); -} // namespace detail - -inline ssize_t write_headers(Stream &strm, const Headers &headers) { - ssize_t write_len = 0; - for (const auto &x : headers) { - auto len = - strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); - if (len < 0) { return len; } - write_len += len; - } - auto len = strm.write("\r\n"); - if (len < 0) { return len; } - write_len += len; - return write_len; -} - -inline bool write_data(Stream &strm, const char *d, size_t l) { - size_t offset = 0; - while (offset < l) { - auto length = strm.write(d + offset, l - offset); - if (length < 0) { return false; } - offset += static_cast(length); - } - return true; -} - -template -inline bool write_content(Stream &strm, const ContentProvider &content_provider, - size_t offset, size_t length, T is_shutting_down, - Error &error) { - size_t end_offset = offset + length; - auto ok = true; - DataSink data_sink; - - data_sink.write = [&](const char *d, size_t l) -> bool { - if (ok) { - if (strm.is_writable() && write_data(strm, d, l)) { - offset += l; - } else { - ok = false; - } - } - return ok; - }; - - data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; - - while (offset < end_offset && !is_shutting_down()) { - if (!strm.is_writable()) { - error = Error::Write; - return false; - } else if (!content_provider(offset, end_offset - offset, data_sink)) { - error = Error::Canceled; - return false; - } else if (!ok) { - error = Error::Write; - return false; - } - } - - error = Error::Success; - return true; -} - -template -inline bool write_content(Stream &strm, const ContentProvider &content_provider, - size_t offset, size_t length, - const T &is_shutting_down) { - auto error = Error::Success; - return write_content(strm, content_provider, offset, length, is_shutting_down, - error); -} - -template -inline bool -write_content_without_length(Stream &strm, - const ContentProvider &content_provider, - const T &is_shutting_down) { - size_t offset = 0; - auto data_available = true; - auto ok = true; - DataSink data_sink; - - data_sink.write = [&](const char *d, size_t l) -> bool { - if (ok) { - offset += l; - if (!strm.is_writable() || !write_data(strm, d, l)) { ok = false; } - } - return ok; - }; - - data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; - - data_sink.done = [&](void) { data_available = false; }; - - while (data_available && !is_shutting_down()) { - if (!strm.is_writable()) { - return false; - } else if (!content_provider(offset, 0, data_sink)) { - return false; - } else if (!ok) { - return false; - } - } - return true; -} - -template -inline bool -write_content_chunked(Stream &strm, const ContentProvider &content_provider, - const T &is_shutting_down, U &compressor, Error &error) { - size_t offset = 0; - auto data_available = true; - auto ok = true; - DataSink data_sink; - - data_sink.write = [&](const char *d, size_t l) -> bool { - if (ok) { - data_available = l > 0; - offset += l; - - std::string payload; - if (compressor.compress(d, l, false, - [&](const char *data, size_t data_len) { - payload.append(data, data_len); - return true; - })) { - if (!payload.empty()) { - // Emit chunked response header and footer for each chunk - auto chunk = - from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; - if (!strm.is_writable() || - !write_data(strm, chunk.data(), chunk.size())) { - ok = false; - } - } - } else { - ok = false; - } - } - return ok; - }; - - data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; - - auto done_with_trailer = [&](const Headers *trailer) { - if (!ok) { return; } - - data_available = false; - - std::string payload; - if (!compressor.compress(nullptr, 0, true, - [&](const char *data, size_t data_len) { - payload.append(data, data_len); - return true; - })) { - ok = false; - return; - } - - if (!payload.empty()) { - // Emit chunked response header and footer for each chunk - auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; - if (!strm.is_writable() || - !write_data(strm, chunk.data(), chunk.size())) { - ok = false; - return; - } - } - - static const std::string done_marker("0\r\n"); - if (!write_data(strm, done_marker.data(), done_marker.size())) { - ok = false; - } - - // Trailer - if (trailer) { - for (const auto &kv : *trailer) { - std::string field_line = kv.first + ": " + kv.second + "\r\n"; - if (!write_data(strm, field_line.data(), field_line.size())) { - ok = false; - } - } - } - - static const std::string crlf("\r\n"); - if (!write_data(strm, crlf.data(), crlf.size())) { ok = false; } - }; - - data_sink.done = [&](void) { done_with_trailer(nullptr); }; - - data_sink.done_with_trailer = [&](const Headers &trailer) { - done_with_trailer(&trailer); - }; - - while (data_available && !is_shutting_down()) { - if (!strm.is_writable()) { - error = Error::Write; - return false; - } else if (!content_provider(offset, 0, data_sink)) { - error = Error::Canceled; - return false; - } else if (!ok) { - error = Error::Write; - return false; - } - } - - error = Error::Success; - return true; -} - -template -inline bool write_content_chunked(Stream &strm, - const ContentProvider &content_provider, - const T &is_shutting_down, U &compressor) { - auto error = Error::Success; - return write_content_chunked(strm, content_provider, is_shutting_down, - compressor, error); -} - -template -inline bool redirect(T &cli, Request &req, Response &res, - const std::string &path, const std::string &location, - Error &error) { - Request new_req = req; - new_req.path = path; - new_req.redirect_count_ -= 1; - - if (res.status == StatusCode::SeeOther_303 && - (req.method != "GET" && req.method != "HEAD")) { - new_req.method = "GET"; - new_req.body.clear(); - new_req.headers.clear(); - } - - Response new_res; - - auto ret = cli.send(new_req, new_res, error); - if (ret) { - req = new_req; - res = new_res; - - if (res.location.empty()) { res.location = location; } - } return ret; } -inline std::string params_to_query_str(const Params ¶ms) { - std::string query; - - for (auto it = params.begin(); it != params.end(); ++it) { - if (it != params.begin()) { query += "&"; } - query += it->first; - query += "="; - query += encode_query_param(it->second); +template inline void write_headers(Stream &strm, const T &info) { + for (const auto &x : info.headers) { + strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); } - return query; + strm.write("\r\n"); +} + +template +inline void write_content_chunked(Stream &strm, const T &x) { + auto chunked_response = !x.has_header("Content-Length"); + uint64_t offset = 0; + auto data_available = true; + while (data_available) { + auto chunk = x.content_producer(offset); + offset += chunk.size(); + data_available = !chunk.empty(); + + // Emit chunked response header and footer for each chunk + if (chunked_response) { + chunk = from_i_to_hex(chunk.size()) + "\r\n" + chunk + "\r\n"; + } + + if (strm.write(chunk.c_str(), chunk.size()) < 0) { + break; // Stop on error + } + } +} + +inline std::string encode_url(const std::string &s) { + std::string result; + + for (auto i = 0; s[i]; i++) { + switch (s[i]) { + case ' ': result += "%20"; break; + case '+': result += "%2B"; break; + case '\r': result += "%0D"; break; + case '\n': result += "%0A"; break; + case '\'': result += "%27"; break; + case ',': result += "%2C"; break; + case ':': result += "%3A"; break; + case ';': result += "%3B"; break; + default: + auto c = static_cast(s[i]); + if (c >= 0x80) { + result += '%'; + char hex[4]; + size_t len = snprintf(hex, sizeof(hex) - 1, "%02X", c); + assert(len == 2); + result.append(hex, len); + } else { + result += s[i]; + } + break; + } + } + + return result; +} + +inline std::string decode_url(const std::string &s) { + std::string result; + + for (size_t i = 0; i < s.size(); i++) { + if (s[i] == '%' && i + 1 < s.size()) { + if (s[i + 1] == 'u') { + int val = 0; + if (from_hex_to_i(s, i + 2, 4, val)) { + // 4 digits Unicode codes + char buff[4]; + size_t len = to_utf8(val, buff); + if (len > 0) { result.append(buff, len); } + i += 5; // 'u0000' + } else { + result += s[i]; + } + } else { + int val = 0; + if (from_hex_to_i(s, i + 1, 2, val)) { + // 2 digits hex codes + result += val; + i += 2; // '00' + } else { + result += s[i]; + } + } + } else if (s[i] == '+') { + result += ' '; + } else { + result += s[i]; + } + } + + return result; } inline void parse_query_text(const std::string &s, Params ¶ms) { - std::set cache; - split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) { - std::string kv(b, e); - if (cache.find(kv) != cache.end()) { return; } - cache.insert(kv); - + split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) { std::string key; std::string val; - split(b, e, '=', [&](const char *b2, const char *e2) { + split(b, e, '=', [&](const char *b, const char *e) { if (key.empty()) { - key.assign(b2, e2); + key.assign(b, e); } else { - val.assign(b2, e2); + val.assign(b, e); } }); - - if (!key.empty()) { - params.emplace(decode_url(key, true), decode_url(val, true)); - } + params.emplace(key, decode_url(val)); }); } inline bool parse_multipart_boundary(const std::string &content_type, std::string &boundary) { - auto boundary_keyword = "boundary="; - auto pos = content_type.find(boundary_keyword); + auto pos = content_type.find("boundary="); if (pos == std::string::npos) { return false; } - auto end = content_type.find(';', pos); - auto beg = pos + strlen(boundary_keyword); - boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg)); - return !boundary.empty(); + + boundary = content_type.substr(pos + 9); + return true; } -inline void parse_disposition_params(const std::string &s, Params ¶ms) { - std::set cache; - split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) { - std::string kv(b, e); - if (cache.find(kv) != cache.end()) { return; } - cache.insert(kv); +inline bool parse_multipart_formdata(const std::string &boundary, + const std::string &body, + MultipartFiles &files) { + static std::string dash = "--"; + static std::string crlf = "\r\n"; - std::string key; - std::string val; - split(b, e, '=', [&](const char *b2, const char *e2) { - if (key.empty()) { - key.assign(b2, e2); - } else { - val.assign(b2, e2); + static std::regex re_content_type("Content-Type: (.*?)", + std::regex_constants::icase); + + static std::regex re_content_disposition( + "Content-Disposition: form-data; name=\"(.*?)\"(?:; filename=\"(.*?)\")?", + std::regex_constants::icase); + + auto dash_boundary = dash + boundary; + + auto pos = body.find(dash_boundary); + if (pos != 0) { return false; } + + pos += dash_boundary.size(); + + auto next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { return false; } + + pos = next_pos + crlf.size(); + + while (pos < body.size()) { + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { return false; } + + std::string name; + MultipartFile file; + + auto header = body.substr(pos, (next_pos - pos)); + + while (pos != next_pos) { + std::smatch m; + if (std::regex_match(header, m, re_content_type)) { + file.content_type = m[1]; + } else if (std::regex_match(header, m, re_content_disposition)) { + name = m[1]; + file.filename = m[2]; } - }); - if (!key.empty()) { - params.emplace(trim_double_quotes_copy((key)), - trim_double_quotes_copy((val))); + pos = next_pos + crlf.size(); + + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { return false; } + + header = body.substr(pos, (next_pos - pos)); } - }); + + pos = next_pos + crlf.size(); + + next_pos = body.find(crlf + dash_boundary, pos); + + if (next_pos == std::string::npos) { return false; } + + file.offset = pos; + file.length = next_pos - pos; + + pos = next_pos + crlf.size() + dash_boundary.size(); + + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { return false; } + + files.emplace(name, file); + + pos = next_pos + crlf.size(); + } + + return true; } -#ifdef CPPHTTPLIB_NO_EXCEPTIONS -inline bool parse_range_header(const std::string &s, Ranges &ranges) { -#else -inline bool parse_range_header(const std::string &s, Ranges &ranges) try { -#endif - static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); - std::smatch m; - if (std::regex_match(s, m, re_first_range)) { - auto pos = static_cast(m.position(1)); - auto len = static_cast(m.length(1)); - auto all_valid_ranges = true; - split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) { - if (!all_valid_ranges) { return; } - static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))"); - std::cmatch cm; - if (std::regex_match(b, e, cm, re_another_range)) { - ssize_t first = -1; - if (!cm.str(1).empty()) { - first = static_cast(std::stoll(cm.str(1))); - } - - ssize_t last = -1; - if (!cm.str(2).empty()) { - last = static_cast(std::stoll(cm.str(2))); - } - - if (first != -1 && last != -1 && first > last) { - all_valid_ranges = false; - return; - } - ranges.emplace_back(std::make_pair(first, last)); - } - }); - return all_valid_ranges; - } - return false; -#ifdef CPPHTTPLIB_NO_EXCEPTIONS -} -#else -} catch (...) { return false; } -#endif - -class MultipartFormDataParser { -public: - MultipartFormDataParser() = default; - - void set_boundary(std::string &&boundary) { - boundary_ = boundary; - dash_boundary_crlf_ = dash_ + boundary_ + crlf_; - crlf_dash_boundary_ = crlf_ + dash_ + boundary_; - } - - bool is_valid() const { return is_valid_; } - - bool parse(const char *buf, size_t n, const ContentReceiver &content_callback, - const MultipartContentHeader &header_callback) { - - buf_append(buf, n); - - while (buf_size() > 0) { - switch (state_) { - case 0: { // Initial boundary - buf_erase(buf_find(dash_boundary_crlf_)); - if (dash_boundary_crlf_.size() > buf_size()) { return true; } - if (!buf_start_with(dash_boundary_crlf_)) { return false; } - buf_erase(dash_boundary_crlf_.size()); - state_ = 1; - break; - } - case 1: { // New entry - clear_file_info(); - state_ = 2; - break; - } - case 2: { // Headers - auto pos = buf_find(crlf_); - if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; } - while (pos < buf_size()) { - // Empty line - if (pos == 0) { - if (!header_callback(file_)) { - is_valid_ = false; - return false; - } - buf_erase(crlf_.size()); - state_ = 3; - break; - } - - const auto header = buf_head(pos); - - if (!parse_header(header.data(), header.data() + header.size(), - [&](std::string &&, std::string &&) {})) { - is_valid_ = false; - return false; - } - - static const std::string header_content_type = "Content-Type:"; - - if (start_with_case_ignore(header, header_content_type)) { - file_.content_type = - trim_copy(header.substr(header_content_type.size())); - } else { - static const std::regex re_content_disposition( - R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~", - std::regex_constants::icase); - - std::smatch m; - if (std::regex_match(header, m, re_content_disposition)) { - Params params; - parse_disposition_params(m[1], params); - - auto it = params.find("name"); - if (it != params.end()) { - file_.name = it->second; - } else { - is_valid_ = false; - return false; - } - - it = params.find("filename"); - if (it != params.end()) { file_.filename = it->second; } - - it = params.find("filename*"); - if (it != params.end()) { - // Only allow UTF-8 enconnding... - static const std::regex re_rfc5987_encoding( - R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase); - - std::smatch m2; - if (std::regex_match(it->second, m2, re_rfc5987_encoding)) { - file_.filename = decode_url(m2[1], false); // override... - } else { - is_valid_ = false; - return false; - } - } - } - } - buf_erase(pos + crlf_.size()); - pos = buf_find(crlf_); - } - if (state_ != 3) { return true; } - break; - } - case 3: { // Body - if (crlf_dash_boundary_.size() > buf_size()) { return true; } - auto pos = buf_find(crlf_dash_boundary_); - if (pos < buf_size()) { - if (!content_callback(buf_data(), pos)) { - is_valid_ = false; - return false; - } - buf_erase(pos + crlf_dash_boundary_.size()); - state_ = 4; - } else { - auto len = buf_size() - crlf_dash_boundary_.size(); - if (len > 0) { - if (!content_callback(buf_data(), len)) { - is_valid_ = false; - return false; - } - buf_erase(len); - } - return true; - } - break; - } - case 4: { // Boundary - if (crlf_.size() > buf_size()) { return true; } - if (buf_start_with(crlf_)) { - buf_erase(crlf_.size()); - state_ = 1; - } else { - if (dash_.size() > buf_size()) { return true; } - if (buf_start_with(dash_)) { - buf_erase(dash_.size()); - is_valid_ = true; - buf_erase(buf_size()); // Remove epilogue - } else { - return true; - } - } - break; - } - } - } - - return true; - } - -private: - void clear_file_info() { - file_.name.clear(); - file_.filename.clear(); - file_.content_type.clear(); - } - - bool start_with_case_ignore(const std::string &a, - const std::string &b) const { - if (a.size() < b.size()) { return false; } - for (size_t i = 0; i < b.size(); i++) { - if (::tolower(a[i]) != ::tolower(b[i])) { return false; } - } - return true; - } - - const std::string dash_ = "--"; - const std::string crlf_ = "\r\n"; - std::string boundary_; - std::string dash_boundary_crlf_; - std::string crlf_dash_boundary_; - - size_t state_ = 0; - bool is_valid_ = false; - MultipartFormData file_; - - // Buffer - bool start_with(const std::string &a, size_t spos, size_t epos, - const std::string &b) const { - if (epos - spos < b.size()) { return false; } - for (size_t i = 0; i < b.size(); i++) { - if (a[i + spos] != b[i]) { return false; } - } - return true; - } - - size_t buf_size() const { return buf_epos_ - buf_spos_; } - - const char *buf_data() const { return &buf_[buf_spos_]; } - - std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); } - - bool buf_start_with(const std::string &s) const { - return start_with(buf_, buf_spos_, buf_epos_, s); - } - - size_t buf_find(const std::string &s) const { - auto c = s.front(); - - size_t off = buf_spos_; - while (off < buf_epos_) { - auto pos = off; - while (true) { - if (pos == buf_epos_) { return buf_size(); } - if (buf_[pos] == c) { break; } - pos++; - } - - auto remaining_size = buf_epos_ - pos; - if (s.size() > remaining_size) { return buf_size(); } - - if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; } - - off = pos + 1; - } - - return buf_size(); - } - - void buf_append(const char *data, size_t n) { - auto remaining_size = buf_size(); - if (remaining_size > 0 && buf_spos_ > 0) { - for (size_t i = 0; i < remaining_size; i++) { - buf_[i] = buf_[buf_spos_ + i]; - } - } - buf_spos_ = 0; - buf_epos_ = remaining_size; - - if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); } - - for (size_t i = 0; i < n; i++) { - buf_[buf_epos_ + i] = data[i]; - } - buf_epos_ += n; - } - - void buf_erase(size_t size) { buf_spos_ += size; } - - std::string buf_; - size_t buf_spos_ = 0; - size_t buf_epos_ = 0; -}; - inline std::string to_lower(const char *beg, const char *end) { std::string out; auto it = beg; while (it != end) { - out += static_cast(::tolower(*it)); + out += ::tolower(*it); it++; } return out; } -inline std::string random_string(size_t length) { - static const char data[] = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +inline void make_range_header_core(std::string &) {} - // std::random_device might actually be deterministic on some - // platforms, but due to lack of support in the c++ standard library, - // doing better requires either some ugly hacks or breaking portability. - static std::random_device seed_gen; - - // Request 128 bits of entropy for initialization - static std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), - seed_gen()}; - - static std::mt19937 engine(seed_sequence); - - std::string result; - for (size_t i = 0; i < length; i++) { - result += data[engine() % (sizeof(data) - 1)]; - } - return result; +template +inline void make_range_header_core(std::string &field, uint64_t value) { + if (!field.empty()) { field += ", "; } + field += std::to_string(value) + "-"; } -inline std::string make_multipart_data_boundary() { - return "--cpp-httplib-multipart-data-" + detail::random_string(16); +template +inline void make_range_header_core(std::string &field, uint64_t value1, + uint64_t value2, Args... args) { + if (!field.empty()) { field += ", "; } + field += std::to_string(value1) + "-" + std::to_string(value2); + make_range_header_core(field, args...); } -inline bool is_multipart_boundary_chars_valid(const std::string &boundary) { - auto valid = true; - for (size_t i = 0; i < boundary.size(); i++) { - auto c = boundary[i]; - if (!std::isalnum(c) && c != '-' && c != '_') { - valid = false; - break; - } - } - return valid; -} - -template -inline std::string -serialize_multipart_formdata_item_begin(const T &item, - const std::string &boundary) { - std::string body = "--" + boundary + "\r\n"; - body += "Content-Disposition: form-data; name=\"" + item.name + "\""; - if (!item.filename.empty()) { - body += "; filename=\"" + item.filename + "\""; - } - body += "\r\n"; - if (!item.content_type.empty()) { - body += "Content-Type: " + item.content_type + "\r\n"; - } - body += "\r\n"; - - return body; -} - -inline std::string serialize_multipart_formdata_item_end() { return "\r\n"; } - -inline std::string -serialize_multipart_formdata_finish(const std::string &boundary) { - return "--" + boundary + "--\r\n"; -} - -inline std::string -serialize_multipart_formdata_get_content_type(const std::string &boundary) { - return "multipart/form-data; boundary=" + boundary; -} - -inline std::string -serialize_multipart_formdata(const MultipartFormDataItems &items, - const std::string &boundary, bool finish = true) { - std::string body; - - for (const auto &item : items) { - body += serialize_multipart_formdata_item_begin(item, boundary); - body += item.content + serialize_multipart_formdata_item_end(); - } - - if (finish) { body += serialize_multipart_formdata_finish(boundary); } - - return body; -} - -inline bool range_error(Request &req, Response &res) { - if (!req.ranges.empty() && 200 <= res.status && res.status < 300) { - ssize_t contant_len = static_cast( - res.content_length_ ? res.content_length_ : res.body.size()); - - ssize_t prev_first_pos = -1; - ssize_t prev_last_pos = -1; - size_t overwrapping_count = 0; - - // NOTE: The following Range check is based on '14.2. Range' in RFC 9110 - // 'HTTP Semantics' to avoid potential denial-of-service attacks. - // https://www.rfc-editor.org/rfc/rfc9110#section-14.2 - - // Too many ranges - if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return true; } - - for (auto &r : req.ranges) { - auto &first_pos = r.first; - auto &last_pos = r.second; - - if (first_pos == -1 && last_pos == -1) { - first_pos = 0; - last_pos = contant_len; - } - - if (first_pos == -1) { - first_pos = contant_len - last_pos; - last_pos = contant_len - 1; - } - - if (last_pos == -1) { last_pos = contant_len - 1; } - - // Range must be within content length - if (!(0 <= first_pos && first_pos <= last_pos && - last_pos <= contant_len - 1)) { - return true; - } - - // Ranges must be in ascending order - if (first_pos <= prev_first_pos) { return true; } - - // Request must not have more than two overlapping ranges - if (first_pos <= prev_last_pos) { - overwrapping_count++; - if (overwrapping_count > 2) { return true; } - } - - prev_first_pos = (std::max)(prev_first_pos, first_pos); - prev_last_pos = (std::max)(prev_last_pos, last_pos); - } - } - - return false; -} - -inline std::pair -get_range_offset_and_length(Range r, size_t content_length) { - assert(r.first != -1 && r.second != -1); - assert(0 <= r.first && r.first < static_cast(content_length)); - assert(r.first <= r.second && - r.second < static_cast(content_length)); - - return std::make_pair(r.first, static_cast(r.second - r.first) + 1); -} - -inline std::string make_content_range_header_field( - const std::pair &offset_and_length, size_t content_length) { - auto st = offset_and_length.first; - auto ed = st + offset_and_length.second - 1; - - std::string field = "bytes "; - field += std::to_string(st); - field += "-"; - field += std::to_string(ed); - field += "/"; - field += std::to_string(content_length); - return field; -} - -template -bool process_multipart_ranges_data(const Request &req, - const std::string &boundary, - const std::string &content_type, - size_t content_length, SToken stoken, - CToken ctoken, Content content) { - for (size_t i = 0; i < req.ranges.size(); i++) { - ctoken("--"); - stoken(boundary); - ctoken("\r\n"); - if (!content_type.empty()) { - ctoken("Content-Type: "); - stoken(content_type); - ctoken("\r\n"); - } - - auto offset_and_length = - get_range_offset_and_length(req.ranges[i], content_length); - - ctoken("Content-Range: "); - stoken(make_content_range_header_field(offset_and_length, content_length)); - ctoken("\r\n"); - ctoken("\r\n"); - - if (!content(offset_and_length.first, offset_and_length.second)) { - return false; - } - ctoken("\r\n"); - } - - ctoken("--"); - stoken(boundary); - ctoken("--"); - - return true; -} - -inline void make_multipart_ranges_data(const Request &req, Response &res, - const std::string &boundary, - const std::string &content_type, - size_t content_length, - std::string &data) { - process_multipart_ranges_data( - req, boundary, content_type, content_length, - [&](const std::string &token) { data += token; }, - [&](const std::string &token) { data += token; }, - [&](size_t offset, size_t length) { - assert(offset + length <= content_length); - data += res.body.substr(offset, length); - return true; - }); -} - -inline size_t get_multipart_ranges_data_length(const Request &req, - const std::string &boundary, - const std::string &content_type, - size_t content_length) { - size_t data_length = 0; - - process_multipart_ranges_data( - req, boundary, content_type, content_length, - [&](const std::string &token) { data_length += token.size(); }, - [&](const std::string &token) { data_length += token.size(); }, - [&](size_t /*offset*/, size_t length) { - data_length += length; - return true; - }); - - return data_length; -} - -template -inline bool -write_multipart_ranges_data(Stream &strm, const Request &req, Response &res, - const std::string &boundary, - const std::string &content_type, - size_t content_length, const T &is_shutting_down) { - return process_multipart_ranges_data( - req, boundary, content_type, content_length, - [&](const std::string &token) { strm.write(token); }, - [&](const std::string &token) { strm.write(token); }, - [&](size_t offset, size_t length) { - return write_content(strm, res.content_provider_, offset, length, - is_shutting_down); - }); -} - -inline bool expect_content(const Request &req) { - if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || - req.method == "PRI" || req.method == "DELETE") { - return true; - } - // TODO: check if Content-Length is set - return false; -} - -inline bool has_crlf(const std::string &s) { - auto p = s.c_str(); - while (*p) { - if (*p == '\r' || *p == '\n') { return true; } - p++; - } - return false; -} - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -inline std::string message_digest(const std::string &s, const EVP_MD *algo) { - auto context = std::unique_ptr( - EVP_MD_CTX_new(), EVP_MD_CTX_free); - - unsigned int hash_length = 0; - unsigned char hash[EVP_MAX_MD_SIZE]; - - EVP_DigestInit_ex(context.get(), algo, nullptr); - EVP_DigestUpdate(context.get(), s.c_str(), s.size()); - EVP_DigestFinal_ex(context.get(), hash, &hash_length); - - std::stringstream ss; - for (auto i = 0u; i < hash_length; ++i) { - ss << std::hex << std::setw(2) << std::setfill('0') - << static_cast(hash[i]); - } - - return ss.str(); -} - -inline std::string MD5(const std::string &s) { - return message_digest(s, EVP_md5()); -} - -inline std::string SHA_256(const std::string &s) { - return message_digest(s, EVP_sha256()); -} - -inline std::string SHA_512(const std::string &s) { - return message_digest(s, EVP_sha512()); -} -#endif - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -#ifdef _WIN32 -// NOTE: This code came up with the following stackoverflow post: -// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store -inline bool load_system_certs_on_windows(X509_STORE *store) { - auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT"); - if (!hStore) { return false; } - - auto result = false; - PCCERT_CONTEXT pContext = NULL; - while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != - nullptr) { - auto encoded_cert = - static_cast(pContext->pbCertEncoded); - - auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); - if (x509) { - X509_STORE_add_cert(store, x509); - X509_free(x509); - result = true; - } - } - - CertFreeCertificateContext(pContext); - CertCloseStore(hStore, 0); - - return result; -} -#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__) -#if TARGET_OS_OSX -template -using CFObjectPtr = - std::unique_ptr::type, void (*)(CFTypeRef)>; - -inline void cf_object_ptr_deleter(CFTypeRef obj) { - if (obj) { CFRelease(obj); } -} - -inline bool retrieve_certs_from_keychain(CFObjectPtr &certs) { - CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef}; - CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll, - kCFBooleanTrue}; - - CFObjectPtr query( - CFDictionaryCreate(nullptr, reinterpret_cast(keys), values, - sizeof(keys) / sizeof(keys[0]), - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks), - cf_object_ptr_deleter); - - if (!query) { return false; } - - CFTypeRef security_items = nullptr; - if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess || - CFArrayGetTypeID() != CFGetTypeID(security_items)) { - return false; - } - - certs.reset(reinterpret_cast(security_items)); - return true; -} - -inline bool retrieve_root_certs_from_keychain(CFObjectPtr &certs) { - CFArrayRef root_security_items = nullptr; - if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) { - return false; - } - - certs.reset(root_security_items); - return true; -} - -inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) { - auto result = false; - for (auto i = 0; i < CFArrayGetCount(certs); ++i) { - const auto cert = reinterpret_cast( - CFArrayGetValueAtIndex(certs, i)); - - if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; } - - CFDataRef cert_data = nullptr; - if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) != - errSecSuccess) { - continue; - } - - CFObjectPtr cert_data_ptr(cert_data, cf_object_ptr_deleter); - - auto encoded_cert = static_cast( - CFDataGetBytePtr(cert_data_ptr.get())); - - auto x509 = - d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get())); - - if (x509) { - X509_STORE_add_cert(store, x509); - X509_free(x509); - result = true; - } - } - - return result; -} - -inline bool load_system_certs_on_macos(X509_STORE *store) { - auto result = false; - CFObjectPtr certs(nullptr, cf_object_ptr_deleter); - if (retrieve_certs_from_keychain(certs) && certs) { - result = add_certs_to_x509_store(certs.get(), store); - } - - if (retrieve_root_certs_from_keychain(certs) && certs) { - result = add_certs_to_x509_store(certs.get(), store) || result; - } - - return result; -} -#endif // TARGET_OS_OSX -#endif // _WIN32 -#endif // CPPHTTPLIB_OPENSSL_SUPPORT - #ifdef _WIN32 class WSInit { public: WSInit() { WSADATA wsaData; - if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true; + WSAStartup(0x0002, &wsaData); } - ~WSInit() { - if (is_valid_) WSACleanup(); - } - - bool is_valid_ = false; + ~WSInit() { WSACleanup(); } }; static WSInit wsinit_; #endif -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -inline std::pair make_digest_authentication_header( - const Request &req, const std::map &auth, - size_t cnonce_count, const std::string &cnonce, const std::string &username, - const std::string &password, bool is_proxy = false) { - std::string nc; - { - std::stringstream ss; - ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count; - nc = ss.str(); - } - - std::string qop; - if (auth.find("qop") != auth.end()) { - qop = auth.at("qop"); - if (qop.find("auth-int") != std::string::npos) { - qop = "auth-int"; - } else if (qop.find("auth") != std::string::npos) { - qop = "auth"; - } else { - qop.clear(); - } - } - - std::string algo = "MD5"; - if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); } - - std::string response; - { - auto H = algo == "SHA-256" ? detail::SHA_256 - : algo == "SHA-512" ? detail::SHA_512 - : detail::MD5; - - auto A1 = username + ":" + auth.at("realm") + ":" + password; - - auto A2 = req.method + ":" + req.path; - if (qop == "auth-int") { A2 += ":" + H(req.body); } - - if (qop.empty()) { - response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2)); - } else { - response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce + - ":" + qop + ":" + H(A2)); - } - } - - auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : ""; - - auto field = "Digest username=\"" + username + "\", realm=\"" + - auth.at("realm") + "\", nonce=\"" + auth.at("nonce") + - "\", uri=\"" + req.path + "\", algorithm=" + algo + - (qop.empty() ? ", response=\"" - : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" + - cnonce + "\", response=\"") + - response + "\"" + - (opaque.empty() ? "" : ", opaque=\"" + opaque + "\""); - - auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; - return std::make_pair(key, field); -} -#endif - -inline bool parse_www_authenticate(const Response &res, - std::map &auth, - bool is_proxy) { - auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate"; - if (res.has_header(auth_key)) { - static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~"); - auto s = res.get_header_value(auth_key); - auto pos = s.find(' '); - if (pos != std::string::npos) { - auto type = s.substr(0, pos); - if (type == "Basic") { - return false; - } else if (type == "Digest") { - s = s.substr(pos + 1); - auto beg = std::sregex_iterator(s.begin(), s.end(), re); - for (auto i = beg; i != std::sregex_iterator(); ++i) { - const auto &m = *i; - auto key = s.substr(static_cast(m.position(1)), - static_cast(m.length(1))); - auto val = m.length(2) > 0 - ? s.substr(static_cast(m.position(2)), - static_cast(m.length(2))) - : s.substr(static_cast(m.position(3)), - static_cast(m.length(3))); - auth[key] = val; - } - return true; - } - } - } - return false; -} - -class ContentProviderAdapter { -public: - explicit ContentProviderAdapter( - ContentProviderWithoutLength &&content_provider) - : content_provider_(content_provider) {} - - bool operator()(size_t offset, size_t, DataSink &sink) { - return content_provider_(offset, sink); - } - -private: - ContentProviderWithoutLength content_provider_; -}; - } // namespace detail -inline std::string hosted_at(const std::string &hostname) { - std::vector addrs; - hosted_at(hostname, addrs); - if (addrs.empty()) { return std::string(); } - return addrs[0]; -} - -inline void hosted_at(const std::string &hostname, - std::vector &addrs) { - struct addrinfo hints; - struct addrinfo *result; - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - - if (getaddrinfo(hostname.c_str(), nullptr, &hints, &result)) { -#if defined __linux__ && !defined __ANDROID__ - res_init(); -#endif - return; - } - - for (auto rp = result; rp; rp = rp->ai_next) { - const auto &addr = - *reinterpret_cast(rp->ai_addr); - std::string ip; - auto dummy = -1; - if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip, - dummy)) { - addrs.push_back(ip); - } - } - - freeaddrinfo(result); -} - -inline std::string append_query_params(const std::string &path, - const Params ¶ms) { - std::string path_with_query = path; - const static std::regex re("[^?]+\\?.*"); - auto delm = std::regex_match(path, re) ? '&' : '?'; - path_with_query += delm + detail::params_to_query_str(params); - return path_with_query; -} - // Header utilities -inline std::pair -make_range_header(const Ranges &ranges) { - std::string field = "bytes="; - auto i = 0; - for (const auto &r : ranges) { - if (i != 0) { field += ", "; } - if (r.first != -1) { field += std::to_string(r.first); } - field += '-'; - if (r.second != -1) { field += std::to_string(r.second); } - i++; - } - return std::make_pair("Range", std::move(field)); +template +inline std::pair make_range_header(uint64_t value, + Args... args) { + std::string field; + detail::make_range_header_core(field, value, args...); + field.insert(0, "bytes="); + return std::make_pair("Range", field); } -inline std::pair -make_basic_authentication_header(const std::string &username, - const std::string &password, bool is_proxy) { + +inline std::pair +make_basic_authentication_header(const std::string& username, const std::string& password) { auto field = "Basic " + detail::base64_encode(username + ":" + password); - auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; - return std::make_pair(key, std::move(field)); + return std::make_pair("Authorization", field); } - -inline std::pair -make_bearer_token_authentication_header(const std::string &token, - bool is_proxy = false) { - auto field = "Bearer " + token; - auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; - return std::make_pair(key, std::move(field)); -} - // Request implementation -inline bool Request::has_header(const std::string &key) const { +inline bool Request::has_header(const char *key) const { return detail::has_header(headers, key); } -inline std::string Request::get_header_value(const std::string &key, - size_t id) const { +inline std::string Request::get_header_value(const char *key, size_t id) const { return detail::get_header_value(headers, key, id, ""); } -inline size_t Request::get_header_value_count(const std::string &key) const { +inline size_t Request::get_header_value_count(const char *key) const { auto r = headers.equal_range(key); - return static_cast(std::distance(r.first, r.second)); + return std::distance(r.first, r.second); } -inline void Request::set_header(const std::string &key, - const std::string &val) { - if (!detail::has_crlf(key) && !detail::has_crlf(val)) { - headers.emplace(key, val); - } +inline void Request::set_header(const char *key, const char *val) { + headers.emplace(key, val); } -inline bool Request::has_param(const std::string &key) const { +inline bool Request::has_param(const char *key) const { return params.find(key) != params.end(); } -inline std::string Request::get_param_value(const std::string &key, - size_t id) const { - auto rng = params.equal_range(key); - auto it = rng.first; - std::advance(it, static_cast(id)); - if (it != rng.second) { return it->second; } +inline std::string Request::get_param_value(const char *key, size_t id) const { + auto it = params.find(key); + std::advance(it, id); + if (it != params.end()) { return it->second; } return std::string(); } -inline size_t Request::get_param_value_count(const std::string &key) const { +inline size_t Request::get_param_value_count(const char *key) const { auto r = params.equal_range(key); - return static_cast(std::distance(r.first, r.second)); + return std::distance(r.first, r.second); } -inline bool Request::is_multipart_form_data() const { - const auto &content_type = get_header_value("Content-Type"); - return !content_type.rfind("multipart/form-data", 0); -} - -inline bool Request::has_file(const std::string &key) const { +inline bool Request::has_file(const char *key) const { return files.find(key) != files.end(); } -inline MultipartFormData Request::get_file_value(const std::string &key) const { +inline MultipartFile Request::get_file_value(const char *key) const { auto it = files.find(key); if (it != files.end()) { return it->second; } - return MultipartFormData(); -} - -inline std::vector -Request::get_file_values(const std::string &key) const { - std::vector values; - auto rng = files.equal_range(key); - for (auto it = rng.first; it != rng.second; it++) { - values.push_back(it->second); - } - return values; + return MultipartFile(); } // Response implementation -inline bool Response::has_header(const std::string &key) const { +inline bool Response::has_header(const char *key) const { return headers.find(key) != headers.end(); } -inline std::string Response::get_header_value(const std::string &key, +inline std::string Response::get_header_value(const char *key, size_t id) const { return detail::get_header_value(headers, key, id, ""); } -inline size_t Response::get_header_value_count(const std::string &key) const { +inline size_t Response::get_header_value_count(const char *key) const { auto r = headers.equal_range(key); - return static_cast(std::distance(r.first, r.second)); + return std::distance(r.first, r.second); } -inline void Response::set_header(const std::string &key, - const std::string &val) { - if (!detail::has_crlf(key) && !detail::has_crlf(val)) { - headers.emplace(key, val); - } +inline void Response::set_header(const char *key, const char *val) { + headers.emplace(key, val); } -inline void Response::set_redirect(const std::string &url, int stat) { - if (!detail::has_crlf(url)) { - set_header("Location", url); - if (300 <= stat && stat < 400) { - this->status = stat; - } else { - this->status = StatusCode::Found_302; - } - } +inline void Response::set_redirect(const char *url) { + set_header("Location", url); + status = 302; } inline void Response::set_content(const char *s, size_t n, - const std::string &content_type) { + const char *content_type) { body.assign(s, n); - - auto rng = headers.equal_range("Content-Type"); - headers.erase(rng.first, rng.second); set_header("Content-Type", content_type); } inline void Response::set_content(const std::string &s, - const std::string &content_type) { - set_content(s.data(), s.size(), content_type); -} - -inline void Response::set_content(std::string &&s, - const std::string &content_type) { - body = std::move(s); - - auto rng = headers.equal_range("Content-Type"); - headers.erase(rng.first, rng.second); + const char *content_type) { + body = s; set_header("Content-Type", content_type); } -inline void Response::set_content_provider( - size_t in_length, const std::string &content_type, ContentProvider provider, - ContentProviderResourceReleaser resource_releaser) { - set_header("Content-Type", content_type); - content_length_ = in_length; - if (in_length > 0) { content_provider_ = std::move(provider); } - content_provider_resource_releaser_ = std::move(resource_releaser); - is_chunked_content_provider_ = false; +// Rstream implementation +template +inline void Stream::write_format(const char *fmt, const Args &... args) { + const auto bufsiz = 2048; + char buf[bufsiz]; + +#if defined(_MSC_VER) && _MSC_VER < 1900 + auto n = _snprintf_s(buf, bufsiz, bufsiz - 1, fmt, args...); +#else + auto n = snprintf(buf, bufsiz - 1, fmt, args...); +#endif + if (n > 0) { + if (n >= bufsiz - 1) { + std::vector glowable_buf(bufsiz); + + while (n >= static_cast(glowable_buf.size() - 1)) { + glowable_buf.resize(glowable_buf.size() * 2); +#if defined(_MSC_VER) && _MSC_VER < 1900 + n = _snprintf_s(&glowable_buf[0], glowable_buf.size(), + glowable_buf.size() - 1, fmt, args...); +#else + n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...); +#endif + } + write(&glowable_buf[0], n); + } else { + write(buf, n); + } + } } -inline void Response::set_content_provider( - const std::string &content_type, ContentProviderWithoutLength provider, - ContentProviderResourceReleaser resource_releaser) { - set_header("Content-Type", content_type); - content_length_ = 0; - content_provider_ = detail::ContentProviderAdapter(std::move(provider)); - content_provider_resource_releaser_ = std::move(resource_releaser); - is_chunked_content_provider_ = false; +// Socket stream implementation +inline SocketStream::SocketStream(socket_t sock) : sock_(sock) {} + +inline SocketStream::~SocketStream() {} + +inline int SocketStream::read(char *ptr, size_t size) { + if (detail::select_read(sock_, CPPHTTPLIB_READ_TIMEOUT_SECOND, + CPPHTTPLIB_READ_TIMEOUT_USECOND) > 0) { + return recv(sock_, ptr, static_cast(size), 0); + } + return -1; } -inline void Response::set_chunked_content_provider( - const std::string &content_type, ContentProviderWithoutLength provider, - ContentProviderResourceReleaser resource_releaser) { - set_header("Content-Type", content_type); - content_length_ = 0; - content_provider_ = detail::ContentProviderAdapter(std::move(provider)); - content_provider_resource_releaser_ = std::move(resource_releaser); - is_chunked_content_provider_ = true; +inline int SocketStream::write(const char *ptr, size_t size) { + return send(sock_, ptr, static_cast(size), 0); } -// Result implementation -inline bool Result::has_request_header(const std::string &key) const { - return request_headers_.find(key) != request_headers_.end(); -} - -inline std::string Result::get_request_header_value(const std::string &key, - size_t id) const { - return detail::get_header_value(request_headers_, key, id, ""); -} - -inline size_t -Result::get_request_header_value_count(const std::string &key) const { - auto r = request_headers_.equal_range(key); - return static_cast(std::distance(r.first, r.second)); -} - -// Stream implementation -inline ssize_t Stream::write(const char *ptr) { +inline int SocketStream::write(const char *ptr) { return write(ptr, strlen(ptr)); } -inline ssize_t Stream::write(const std::string &s) { - return write(s.data(), s.size()); +inline std::string SocketStream::get_remote_addr() const { + return detail::get_remote_addr(sock_); } -namespace detail { - -// Socket stream implementation -inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, - time_t read_timeout_usec, - time_t write_timeout_sec, - time_t write_timeout_usec) - : sock_(sock), read_timeout_sec_(read_timeout_sec), - read_timeout_usec_(read_timeout_usec), - write_timeout_sec_(write_timeout_sec), - write_timeout_usec_(write_timeout_usec), read_buff_(read_buff_size_, 0) {} - -inline SocketStream::~SocketStream() = default; - -inline bool SocketStream::is_readable() const { - return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; -} - -inline bool SocketStream::is_writable() const { - return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && - is_socket_alive(sock_); -} - -inline ssize_t SocketStream::read(char *ptr, size_t size) { -#ifdef _WIN32 - size = - (std::min)(size, static_cast((std::numeric_limits::max)())); -#else - size = (std::min)(size, - static_cast((std::numeric_limits::max)())); -#endif - - if (read_buff_off_ < read_buff_content_size_) { - auto remaining_size = read_buff_content_size_ - read_buff_off_; - if (size <= remaining_size) { - memcpy(ptr, read_buff_.data() + read_buff_off_, size); - read_buff_off_ += size; - return static_cast(size); - } else { - memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size); - read_buff_off_ += remaining_size; - return static_cast(remaining_size); - } - } - - if (!is_readable()) { return -1; } - - read_buff_off_ = 0; - read_buff_content_size_ = 0; - - if (size < read_buff_size_) { - auto n = read_socket(sock_, read_buff_.data(), read_buff_size_, - CPPHTTPLIB_RECV_FLAGS); - if (n <= 0) { - return n; - } else if (n <= static_cast(size)) { - memcpy(ptr, read_buff_.data(), static_cast(n)); - return n; - } else { - memcpy(ptr, read_buff_.data(), size); - read_buff_off_ = size; - read_buff_content_size_ = static_cast(n); - return static_cast(size); - } - } else { - return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS); - } -} - -inline ssize_t SocketStream::write(const char *ptr, size_t size) { - if (!is_writable()) { return -1; } - -#if defined(_WIN32) && !defined(_WIN64) - size = - (std::min)(size, static_cast((std::numeric_limits::max)())); -#endif - - return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS); -} - -inline void SocketStream::get_remote_ip_and_port(std::string &ip, - int &port) const { - return detail::get_remote_ip_and_port(sock_, ip, port); -} - -inline void SocketStream::get_local_ip_and_port(std::string &ip, - int &port) const { - return detail::get_local_ip_and_port(sock_, ip, port); -} - -inline socket_t SocketStream::socket() const { return sock_; } - // Buffer stream implementation -inline bool BufferStream::is_readable() const { return true; } - -inline bool BufferStream::is_writable() const { return true; } - -inline ssize_t BufferStream::read(char *ptr, size_t size) { -#if defined(_MSC_VER) && _MSC_VER < 1910 - auto len_read = buffer._Copy_s(ptr, size, size, position); +inline int BufferStream::read(char *ptr, size_t size) { +#if defined(_MSC_VER) && _MSC_VER < 1900 + return static_cast(buffer._Copy_s(ptr, size, size)); #else - auto len_read = buffer.copy(ptr, size, position); + return static_cast(buffer.copy(ptr, size)); #endif - position += static_cast(len_read); - return static_cast(len_read); } -inline ssize_t BufferStream::write(const char *ptr, size_t size) { +inline int BufferStream::write(const char *ptr, size_t size) { buffer.append(ptr, size); - return static_cast(size); + return static_cast(size); } -inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/, - int & /*port*/) const {} +inline int BufferStream::write(const char *ptr) { + size_t size = strlen(ptr); + buffer.append(ptr, size); + return static_cast(size); +} -inline void BufferStream::get_local_ip_and_port(std::string & /*ip*/, - int & /*port*/) const {} - -inline socket_t BufferStream::socket() const { return 0; } +inline std::string BufferStream::get_remote_addr() const { return ""; } inline const std::string &BufferStream::get_buffer() const { return buffer; } -inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) { - // One past the last ending position of a path param substring - std::size_t last_param_end = 0; - -#ifndef CPPHTTPLIB_NO_EXCEPTIONS - // Needed to ensure that parameter names are unique during matcher - // construction - // If exceptions are disabled, only last duplicate path - // parameter will be set - std::unordered_set param_name_set; -#endif - - while (true) { - const auto marker_pos = pattern.find(marker, last_param_end); - if (marker_pos == std::string::npos) { break; } - - static_fragments_.push_back( - pattern.substr(last_param_end, marker_pos - last_param_end)); - - const auto param_name_start = marker_pos + 1; - - auto sep_pos = pattern.find(separator, param_name_start); - if (sep_pos == std::string::npos) { sep_pos = pattern.length(); } - - auto param_name = - pattern.substr(param_name_start, sep_pos - param_name_start); - -#ifndef CPPHTTPLIB_NO_EXCEPTIONS - if (param_name_set.find(param_name) != param_name_set.cend()) { - std::string msg = "Encountered path parameter '" + param_name + - "' multiple times in route pattern '" + pattern + "'."; - throw std::invalid_argument(msg); - } -#endif - - param_names_.push_back(std::move(param_name)); - - last_param_end = sep_pos + 1; - } - - if (last_param_end < pattern.length()) { - static_fragments_.push_back(pattern.substr(last_param_end)); - } -} - -inline bool PathParamsMatcher::match(Request &request) const { - request.matches = std::smatch(); - request.path_params.clear(); - request.path_params.reserve(param_names_.size()); - - // One past the position at which the path matched the pattern last time - std::size_t starting_pos = 0; - for (size_t i = 0; i < static_fragments_.size(); ++i) { - const auto &fragment = static_fragments_[i]; - - if (starting_pos + fragment.length() > request.path.length()) { - return false; - } - - // Avoid unnecessary allocation by using strncmp instead of substr + - // comparison - if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(), - fragment.length()) != 0) { - return false; - } - - starting_pos += fragment.length(); - - // Should only happen when we have a static fragment after a param - // Example: '/users/:id/subscriptions' - // The 'subscriptions' fragment here does not have a corresponding param - if (i >= param_names_.size()) { continue; } - - auto sep_pos = request.path.find(separator, starting_pos); - if (sep_pos == std::string::npos) { sep_pos = request.path.length(); } - - const auto ¶m_name = param_names_[i]; - - request.path_params.emplace( - param_name, request.path.substr(starting_pos, sep_pos - starting_pos)); - - // Mark everythin up to '/' as matched - starting_pos = sep_pos + 1; - } - // Returns false if the path is longer than the pattern - return starting_pos >= request.path.length(); -} - -inline bool RegexMatcher::match(Request &request) const { - request.path_params.clear(); - return std::regex_match(request.path, request.matches, regex_); -} - -} // namespace detail - // HTTP server implementation inline Server::Server() - : new_task_queue( - [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) { + : keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), + payload_max_length_(CPPHTTPLIB_PAYLOAD_MAX_LENGTH), is_running_(false), + svr_sock_(INVALID_SOCKET), running_threads_(0) { #ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif } -inline Server::~Server() = default; +inline Server::~Server() {} -inline std::unique_ptr -Server::make_matcher(const std::string &pattern) { - if (pattern.find("/:") != std::string::npos) { - return detail::make_unique(pattern); - } else { - return detail::make_unique(pattern); - } -} - -inline Server &Server::Get(const std::string &pattern, Handler handler) { - get_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); +inline Server &Server::Get(const char *pattern, Handler handler) { + get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); return *this; } -inline Server &Server::Post(const std::string &pattern, Handler handler) { - post_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); +inline Server &Server::Post(const char *pattern, Handler handler) { + post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); return *this; } -inline Server &Server::Post(const std::string &pattern, - HandlerWithContentReader handler) { - post_handlers_for_content_reader_.emplace_back(make_matcher(pattern), - std::move(handler)); +inline Server &Server::Put(const char *pattern, Handler handler) { + put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); return *this; } -inline Server &Server::Put(const std::string &pattern, Handler handler) { - put_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); +inline Server &Server::Patch(const char *pattern, Handler handler) { + patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); return *this; } -inline Server &Server::Put(const std::string &pattern, - HandlerWithContentReader handler) { - put_handlers_for_content_reader_.emplace_back(make_matcher(pattern), - std::move(handler)); +inline Server &Server::Delete(const char *pattern, Handler handler) { + delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); return *this; } -inline Server &Server::Patch(const std::string &pattern, Handler handler) { - patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); +inline Server &Server::Options(const char *pattern, Handler handler) { + options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); return *this; } -inline Server &Server::Patch(const std::string &pattern, - HandlerWithContentReader handler) { - patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern), - std::move(handler)); - return *this; -} - -inline Server &Server::Delete(const std::string &pattern, Handler handler) { - delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); - return *this; -} - -inline Server &Server::Delete(const std::string &pattern, - HandlerWithContentReader handler) { - delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern), - std::move(handler)); - return *this; -} - -inline Server &Server::Options(const std::string &pattern, Handler handler) { - options_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); - return *this; -} - -inline bool Server::set_base_dir(const std::string &dir, - const std::string &mount_point) { - return set_mount_point(mount_point, dir); -} - -inline bool Server::set_mount_point(const std::string &mount_point, - const std::string &dir, Headers headers) { - if (detail::is_dir(dir)) { - std::string mnt = !mount_point.empty() ? mount_point : "/"; - if (!mnt.empty() && mnt[0] == '/') { - base_dirs_.push_back({mnt, dir, std::move(headers)}); - return true; - } +inline bool Server::set_base_dir(const char *path) { + if (detail::is_dir(path)) { + base_dir_ = path; + return true; } return false; } -inline bool Server::remove_mount_point(const std::string &mount_point) { - for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) { - if (it->mount_point == mount_point) { - base_dirs_.erase(it); - return true; - } - } - return false; +inline void Server::set_error_handler(Handler handler) { + error_handler_ = handler; } -inline Server & -Server::set_file_extension_and_mimetype_mapping(const std::string &ext, - const std::string &mime) { - file_extension_and_mimetype_map_[ext] = mime; - return *this; -} +inline void Server::set_logger(Logger logger) { logger_ = logger; } -inline Server &Server::set_default_file_mimetype(const std::string &mime) { - default_file_mimetype_ = mime; - return *this; -} - -inline Server &Server::set_file_request_handler(Handler handler) { - file_request_handler_ = std::move(handler); - return *this; -} - -inline Server &Server::set_error_handler(HandlerWithResponse handler) { - error_handler_ = std::move(handler); - return *this; -} - -inline Server &Server::set_error_handler(Handler handler) { - error_handler_ = [handler](const Request &req, Response &res) { - handler(req, res); - return HandlerResponse::Handled; - }; - return *this; -} - -inline Server &Server::set_exception_handler(ExceptionHandler handler) { - exception_handler_ = std::move(handler); - return *this; -} - -inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) { - pre_routing_handler_ = std::move(handler); - return *this; -} - -inline Server &Server::set_post_routing_handler(Handler handler) { - post_routing_handler_ = std::move(handler); - return *this; -} - -inline Server &Server::set_logger(Logger logger) { - logger_ = std::move(logger); - return *this; -} - -inline Server & -Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) { - expect_100_continue_handler_ = std::move(handler); - return *this; -} - -inline Server &Server::set_address_family(int family) { - address_family_ = family; - return *this; -} - -inline Server &Server::set_tcp_nodelay(bool on) { - tcp_nodelay_ = on; - return *this; -} - -inline Server &Server::set_socket_options(SocketOptions socket_options) { - socket_options_ = std::move(socket_options); - return *this; -} - -inline Server &Server::set_default_headers(Headers headers) { - default_headers_ = std::move(headers); - return *this; -} - -inline Server &Server::set_header_writer( - std::function const &writer) { - header_writer_ = writer; - return *this; -} - -inline Server &Server::set_keep_alive_max_count(size_t count) { +inline void Server::set_keep_alive_max_count(size_t count) { keep_alive_max_count_ = count; - return *this; } -inline Server &Server::set_keep_alive_timeout(time_t sec) { - keep_alive_timeout_sec_ = sec; - return *this; -} - -inline Server &Server::set_read_timeout(time_t sec, time_t usec) { - read_timeout_sec_ = sec; - read_timeout_usec_ = usec; - return *this; -} - -inline Server &Server::set_write_timeout(time_t sec, time_t usec) { - write_timeout_sec_ = sec; - write_timeout_usec_ = usec; - return *this; -} - -inline Server &Server::set_idle_interval(time_t sec, time_t usec) { - idle_interval_sec_ = sec; - idle_interval_usec_ = usec; - return *this; -} - -inline Server &Server::set_payload_max_length(size_t length) { +inline void Server::set_payload_max_length(uint64_t length) { payload_max_length_ = length; - return *this; } -inline bool Server::bind_to_port(const std::string &host, int port, - int socket_flags) { - return bind_internal(host, port, socket_flags) >= 0; -} -inline int Server::bind_to_any_port(const std::string &host, int socket_flags) { +inline int Server::bind_to_any_port(const char *host, int socket_flags) { return bind_internal(host, 0, socket_flags); } -inline bool Server::listen_after_bind() { - auto se = detail::scope_exit([&]() { done_ = true; }); +inline bool Server::listen_after_bind() { return listen_internal(); } + +inline bool Server::listen(const char *host, int port, int socket_flags) { + if (bind_internal(host, port, socket_flags) < 0) return false; return listen_internal(); } -inline bool Server::listen(const std::string &host, int port, - int socket_flags) { - auto se = detail::scope_exit([&]() { done_ = true; }); - return bind_to_port(host, port, socket_flags) && listen_internal(); -} - inline bool Server::is_running() const { return is_running_; } -inline void Server::wait_until_ready() const { - while (!is_running() && !done_) { - std::this_thread::sleep_for(std::chrono::milliseconds{1}); - } -} - inline void Server::stop() { if (is_running_) { assert(svr_sock_ != INVALID_SOCKET); @@ -5923,389 +1723,142 @@ inline void Server::stop() { } } -inline bool Server::parse_request_line(const char *s, Request &req) const { - auto len = strlen(s); - if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; } - len -= 2; +inline bool Server::parse_request_line(const char *s, Request &req) { + static std::regex re("(GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS) " + "(([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n"); - { - size_t count = 0; + std::cmatch m; + if (std::regex_match(s, m, re)) { + req.version = std::string(m[5]); + req.method = std::string(m[1]); + req.target = std::string(m[2]); + req.path = detail::decode_url(m[3]); - detail::split(s, s + len, ' ', [&](const char *b, const char *e) { - switch (count) { - case 0: req.method = std::string(b, e); break; - case 1: req.target = std::string(b, e); break; - case 2: req.version = std::string(b, e); break; - default: break; - } - count++; - }); + // Parse query text + auto len = std::distance(m[4].first, m[4].second); + if (len > 0) { detail::parse_query_text(m[4], req.params); } - if (count != 3) { return false; } + return true; } - static const std::set methods{ - "GET", "HEAD", "POST", "PUT", "DELETE", - "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"}; - - if (methods.find(req.method) == methods.end()) { return false; } - - if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") { return false; } - - { - // Skip URL fragment - for (size_t i = 0; i < req.target.size(); i++) { - if (req.target[i] == '#') { - req.target.erase(i); - break; - } - } - - size_t count = 0; - - detail::split(req.target.data(), req.target.data() + req.target.size(), '?', - 2, [&](const char *b, const char *e) { - switch (count) { - case 0: - req.path = detail::decode_url(std::string(b, e), false); - break; - case 1: { - if (e - b > 0) { - detail::parse_query_text(std::string(b, e), req.params); - } - break; - } - default: break; - } - count++; - }); - - if (count > 2) { return false; } - } - - return true; + return false; } -inline bool Server::write_response(Stream &strm, bool close_connection, - Request &req, Response &res) { - // NOTE: `req.ranges` should be empty, otherwise it will be applied - // incorrectly to the error content. - req.ranges.clear(); - return write_response_core(strm, close_connection, req, res, false); -} - -inline bool Server::write_response_with_content(Stream &strm, - bool close_connection, - const Request &req, - Response &res) { - return write_response_core(strm, close_connection, req, res, true); -} - -inline bool Server::write_response_core(Stream &strm, bool close_connection, - const Request &req, Response &res, - bool need_apply_ranges) { +inline void Server::write_response(Stream &strm, bool last_connection, + const Request &req, Response &res) { assert(res.status != -1); - if (400 <= res.status && error_handler_ && - error_handler_(req, res) == HandlerResponse::Handled) { - need_apply_ranges = true; - } + if (400 <= res.status && error_handler_) { error_handler_(req, res); } - std::string content_type; - std::string boundary; - if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); } + // Response line + strm.write_format("HTTP/1.1 %d %s\r\n", res.status, + detail::status_message(res.status)); - // Prepare additional headers - if (close_connection || req.get_header_value("Connection") == "close") { + // Headers + if (last_connection || req.get_header_value("Connection") == "close") { res.set_header("Connection", "close"); + } + + if (!last_connection && req.get_header_value("Connection") == "Keep-Alive") { + res.set_header("Connection", "Keep-Alive"); + } + + if (res.body.empty()) { + if (!res.has_header("Content-Length")) { + if (res.content_producer) { + // Streamed response + res.set_header("Transfer-Encoding", "chunked"); + } else { + res.set_header("Content-Length", "0"); + } + } } else { - std::stringstream ss; - ss << "timeout=" << keep_alive_timeout_sec_ - << ", max=" << keep_alive_max_count_; - res.set_header("Keep-Alive", ss.str()); - } +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + // TODO: 'Accpet-Encoding' has gzip, not gzip;q=0 + const auto &encodings = req.get_header_value("Accept-Encoding"); + if (encodings.find("gzip") != std::string::npos && + detail::can_compress(res.get_header_value("Content-Type"))) { + if (detail::compress(res.body)) { + res.set_header("Content-Encoding", "gzip"); + } + } +#endif - if (!res.has_header("Content-Type") && - (!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) { - res.set_header("Content-Type", "text/plain"); - } - - if (!res.has_header("Content-Length") && res.body.empty() && - !res.content_length_ && !res.content_provider_) { - res.set_header("Content-Length", "0"); - } - - if (!res.has_header("Accept-Ranges") && req.method == "HEAD") { - res.set_header("Accept-Ranges", "bytes"); - } - - if (post_routing_handler_) { post_routing_handler_(req, res); } - - // Response line and headers - { - detail::BufferStream bstrm; - - if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status, - status_message(res.status))) { - return false; + if (!res.has_header("Content-Type")) { + res.set_header("Content-Type", "text/plain"); } - if (!header_writer_(bstrm, res.headers)) { return false; } - - // Flush buffer - auto &data = bstrm.get_buffer(); - detail::write_data(strm, data.data(), data.size()); + auto length = std::to_string(res.body.size()); + res.set_header("Content-Length", length.c_str()); } + detail::write_headers(strm, res); + // Body - auto ret = true; if (req.method != "HEAD") { if (!res.body.empty()) { - if (!detail::write_data(strm, res.body.data(), res.body.size())) { - ret = false; - } - } else if (res.content_provider_) { - if (write_content_with_provider(strm, req, res, boundary, content_type)) { - res.content_provider_success_ = true; - } else { - ret = false; - } + strm.write(res.body.c_str(), res.body.size()); + } else if (res.content_producer) { + detail::write_content_chunked(strm, res); } } // Log if (logger_) { logger_(req, res); } - - return ret; } -inline bool -Server::write_content_with_provider(Stream &strm, const Request &req, - Response &res, const std::string &boundary, - const std::string &content_type) { - auto is_shutting_down = [this]() { - return this->svr_sock_ == INVALID_SOCKET; - }; +inline bool Server::handle_file_request(Request &req, Response &res) { + if (!base_dir_.empty() && detail::is_valid_path(req.path)) { + std::string path = base_dir_ + req.path; - if (res.content_length_ > 0) { - if (req.ranges.empty()) { - return detail::write_content(strm, res.content_provider_, 0, - res.content_length_, is_shutting_down); - } else if (req.ranges.size() == 1) { - auto offset_and_length = detail::get_range_offset_and_length( - req.ranges[0], res.content_length_); + if (!path.empty() && path.back() == '/') { path += "index.html"; } - return detail::write_content(strm, res.content_provider_, - offset_and_length.first, - offset_and_length.second, is_shutting_down); - } else { - return detail::write_multipart_ranges_data( - strm, req, res, boundary, content_type, res.content_length_, - is_shutting_down); - } - } else { - if (res.is_chunked_content_provider_) { - auto type = detail::encoding_type(req, res); - - std::unique_ptr compressor; - if (type == detail::EncodingType::Gzip) { -#ifdef CPPHTTPLIB_ZLIB_SUPPORT - compressor = detail::make_unique(); -#endif - } else if (type == detail::EncodingType::Brotli) { -#ifdef CPPHTTPLIB_BROTLI_SUPPORT - compressor = detail::make_unique(); -#endif - } else { - compressor = detail::make_unique(); - } - assert(compressor != nullptr); - - return detail::write_content_chunked(strm, res.content_provider_, - is_shutting_down, *compressor); - } else { - return detail::write_content_without_length(strm, res.content_provider_, - is_shutting_down); - } - } -} - -inline bool Server::read_content(Stream &strm, Request &req, Response &res) { - MultipartFormDataMap::iterator cur; - auto file_count = 0; - if (read_content_core( - strm, req, res, - // Regular - [&](const char *buf, size_t n) { - if (req.body.size() + n > req.body.max_size()) { return false; } - req.body.append(buf, n); - return true; - }, - // Multipart - [&](const MultipartFormData &file) { - if (file_count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) { - return false; - } - cur = req.files.emplace(file.name, file); - return true; - }, - [&](const char *buf, size_t n) { - auto &content = cur->second.content; - if (content.size() + n > content.max_size()) { return false; } - content.append(buf, n); - return true; - })) { - const auto &content_type = req.get_header_value("Content-Type"); - if (!content_type.find("application/x-www-form-urlencoded")) { - if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) { - res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414? - return false; - } - detail::parse_query_text(req.body, req.params); - } - return true; - } - return false; -} - -inline bool Server::read_content_with_content_receiver( - Stream &strm, Request &req, Response &res, ContentReceiver receiver, - MultipartContentHeader multipart_header, - ContentReceiver multipart_receiver) { - return read_content_core(strm, req, res, std::move(receiver), - std::move(multipart_header), - std::move(multipart_receiver)); -} - -inline bool -Server::read_content_core(Stream &strm, Request &req, Response &res, - ContentReceiver receiver, - MultipartContentHeader multipart_header, - ContentReceiver multipart_receiver) const { - detail::MultipartFormDataParser multipart_form_data_parser; - ContentReceiverWithProgress out; - - if (req.is_multipart_form_data()) { - const auto &content_type = req.get_header_value("Content-Type"); - std::string boundary; - if (!detail::parse_multipart_boundary(content_type, boundary)) { - res.status = StatusCode::BadRequest_400; - return false; - } - - multipart_form_data_parser.set_boundary(std::move(boundary)); - out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) { - /* For debug - size_t pos = 0; - while (pos < n) { - auto read_size = (std::min)(1, n - pos); - auto ret = multipart_form_data_parser.parse( - buf + pos, read_size, multipart_receiver, multipart_header); - if (!ret) { return false; } - pos += read_size; - } + if (detail::is_file(path)) { + detail::read_file(path, res.body); + auto type = detail::find_content_type(path); + if (type) { res.set_header("Content-Type", type); } + res.status = 200; return true; - */ - return multipart_form_data_parser.parse(buf, n, multipart_receiver, - multipart_header); - }; - } else { - out = [receiver](const char *buf, size_t n, uint64_t /*off*/, - uint64_t /*len*/) { return receiver(buf, n); }; - } - - if (req.method == "DELETE" && !req.has_header("Content-Length")) { - return true; - } - - if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr, - out, true)) { - return false; - } - - if (req.is_multipart_form_data()) { - if (!multipart_form_data_parser.is_valid()) { - res.status = StatusCode::BadRequest_400; - return false; } } - return true; -} - -inline bool Server::handle_file_request(const Request &req, Response &res, - bool head) { - for (const auto &entry : base_dirs_) { - // Prefix match - if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) { - std::string sub_path = "/" + req.path.substr(entry.mount_point.size()); - if (detail::is_valid_path(sub_path)) { - auto path = entry.base_dir + sub_path; - if (path.back() == '/') { path += "index.html"; } - - if (detail::is_file(path)) { - for (const auto &kv : entry.headers) { - res.set_header(kv.first, kv.second); - } - - auto mm = std::make_shared(path.c_str()); - if (!mm->is_open()) { return false; } - - res.set_content_provider( - mm->size(), - detail::find_content_type(path, file_extension_and_mimetype_map_, - default_file_mimetype_), - [mm](size_t offset, size_t length, DataSink &sink) -> bool { - sink.write(mm->data() + offset, length); - return true; - }); - - if (!head && file_request_handler_) { - file_request_handler_(req, res); - } - - return true; - } - } - } - } return false; } -inline socket_t -Server::create_server_socket(const std::string &host, int port, - int socket_flags, - SocketOptions socket_options) const { +inline socket_t Server::create_server_socket(const char *host, int port, + int socket_flags) const { return detail::create_socket( - host, std::string(), port, address_family_, socket_flags, tcp_nodelay_, - std::move(socket_options), + host, port, [](socket_t sock, struct addrinfo &ai) -> bool { - if (::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + if (::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + return false; + } + if (::listen(sock, 5)) { // Listen through 5 channels return false; } - if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) { return false; } return true; - }); + }, + socket_flags); } -inline int Server::bind_internal(const std::string &host, int port, - int socket_flags) { +inline int Server::bind_internal(const char *host, int port, int socket_flags) { if (!is_valid()) { return -1; } - svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_); + svr_sock_ = create_server_socket(host, port, socket_flags); if (svr_sock_ == INVALID_SOCKET) { return -1; } if (port == 0) { - struct sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); - if (getsockname(svr_sock_, reinterpret_cast(&addr), - &addr_len) == -1) { + struct sockaddr_storage address; + socklen_t len = sizeof(address); + if (getsockname(svr_sock_, reinterpret_cast(&address), + &len) == -1) { return -1; } - if (addr.ss_family == AF_INET) { - return ntohs(reinterpret_cast(&addr)->sin_port); - } else if (addr.ss_family == AF_INET6) { - return ntohs(reinterpret_cast(&addr)->sin6_port); + if (address.ss_family == AF_INET) { + return ntohs(reinterpret_cast(&address)->sin_port); + } else if (address.ss_family == AF_INET6) { + return ntohs( + reinterpret_cast(&address)->sin6_port); } else { return -1; } @@ -6316,172 +1869,87 @@ inline int Server::bind_internal(const std::string &host, int port, inline bool Server::listen_internal() { auto ret = true; + is_running_ = true; - auto se = detail::scope_exit([&]() { is_running_ = false; }); - { - std::unique_ptr task_queue(new_task_queue()); - - while (svr_sock_ != INVALID_SOCKET) { -#ifndef _WIN32 - if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) { -#endif - auto val = detail::select_read(svr_sock_, idle_interval_sec_, - idle_interval_usec_); - if (val == 0) { // Timeout - task_queue->on_idle(); - continue; - } -#ifndef _WIN32 - } -#endif - socket_t sock = accept(svr_sock_, nullptr, nullptr); - - if (sock == INVALID_SOCKET) { - if (errno == EMFILE) { - // The per-process limit of open file descriptors has been reached. - // Try to accept new connections after a short sleep. - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } else if (errno == EINTR || errno == EAGAIN) { - continue; - } - if (svr_sock_ != INVALID_SOCKET) { - detail::close_socket(svr_sock_); - ret = false; - } else { - ; // The server socket was closed by user. - } - break; - } - - { -#ifdef _WIN32 - auto timeout = static_cast(read_timeout_sec_ * 1000 + - read_timeout_usec_ / 1000); - setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, - reinterpret_cast(&timeout), sizeof(timeout)); -#else - timeval tv; - tv.tv_sec = static_cast(read_timeout_sec_); - tv.tv_usec = static_cast(read_timeout_usec_); - setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, - reinterpret_cast(&tv), sizeof(tv)); -#endif - } - { - -#ifdef _WIN32 - auto timeout = static_cast(write_timeout_sec_ * 1000 + - write_timeout_usec_ / 1000); - setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, - reinterpret_cast(&timeout), sizeof(timeout)); -#else - timeval tv; - tv.tv_sec = static_cast(write_timeout_sec_); - tv.tv_usec = static_cast(write_timeout_usec_); - setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, - reinterpret_cast(&tv), sizeof(tv)); -#endif - } - - if (!task_queue->enqueue( - [this, sock]() { process_and_close_socket(sock); })) { - detail::shutdown_socket(sock); - detail::close_socket(sock); - } + for (;;) { + if (svr_sock_ == INVALID_SOCKET) { + // The server socket was closed by 'stop' method. + break; } - task_queue->shutdown(); + auto val = detail::select_read(svr_sock_, 0, 100000); + + if (val == 0) { // Timeout + continue; + } + + socket_t sock = accept(svr_sock_, nullptr, nullptr); + + if (sock == INVALID_SOCKET) { + if (svr_sock_ != INVALID_SOCKET) { + detail::close_socket(svr_sock_); + ret = false; + } else { + ; // The server socket was closed by user. + } + break; + } + + // TODO: Use thread pool... + std::thread([=]() { + { + std::lock_guard guard(running_threads_mutex_); + running_threads_++; + } + + read_and_close_socket(sock); + + { + std::lock_guard guard(running_threads_mutex_); + running_threads_--; + } + }).detach(); } + // TODO: Use thread pool... + for (;;) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + std::lock_guard guard(running_threads_mutex_); + if (!running_threads_) { break; } + } + + is_running_ = false; + return ret; } -inline bool Server::routing(Request &req, Response &res, Stream &strm) { - if (pre_routing_handler_ && - pre_routing_handler_(req, res) == HandlerResponse::Handled) { - return true; - } +inline bool Server::routing(Request &req, Response &res) { + if (req.method == "GET" && handle_file_request(req, res)) { return true; } - // File handler - auto is_head_request = req.method == "HEAD"; - if ((req.method == "GET" || is_head_request) && - handle_file_request(req, res, is_head_request)) { - return true; - } - - if (detail::expect_content(req)) { - // Content reader handler - { - ContentReader reader( - [&](ContentReceiver receiver) { - return read_content_with_content_receiver( - strm, req, res, std::move(receiver), nullptr, nullptr); - }, - [&](MultipartContentHeader header, ContentReceiver receiver) { - return read_content_with_content_receiver(strm, req, res, nullptr, - std::move(header), - std::move(receiver)); - }); - - if (req.method == "POST") { - if (dispatch_request_for_content_reader( - req, res, std::move(reader), - post_handlers_for_content_reader_)) { - return true; - } - } else if (req.method == "PUT") { - if (dispatch_request_for_content_reader( - req, res, std::move(reader), - put_handlers_for_content_reader_)) { - return true; - } - } else if (req.method == "PATCH") { - if (dispatch_request_for_content_reader( - req, res, std::move(reader), - patch_handlers_for_content_reader_)) { - return true; - } - } else if (req.method == "DELETE") { - if (dispatch_request_for_content_reader( - req, res, std::move(reader), - delete_handlers_for_content_reader_)) { - return true; - } - } - } - - // Read content into `req.body` - if (!read_content(strm, req, res)) { return false; } - } - - // Regular handler if (req.method == "GET" || req.method == "HEAD") { return dispatch_request(req, res, get_handlers_); } else if (req.method == "POST") { return dispatch_request(req, res, post_handlers_); } else if (req.method == "PUT") { return dispatch_request(req, res, put_handlers_); + } else if (req.method == "PATCH") { + return dispatch_request(req, res, patch_handlers_); } else if (req.method == "DELETE") { return dispatch_request(req, res, delete_handlers_); } else if (req.method == "OPTIONS") { return dispatch_request(req, res, options_handlers_); - } else if (req.method == "PATCH") { - return dispatch_request(req, res, patch_handlers_); } - - res.status = StatusCode::BadRequest_400; return false; } inline bool Server::dispatch_request(Request &req, Response &res, - const Handlers &handlers) const { + Handlers &handlers) { for (const auto &x : handlers) { - const auto &matcher = x.first; + const auto &pattern = x.first; const auto &handler = x.second; - if (matcher->match(req)) { + if (std::regex_match(req.path, req.matches, pattern)) { handler(req, res); return true; } @@ -6489,769 +1957,186 @@ inline bool Server::dispatch_request(Request &req, Response &res, return false; } -inline void Server::apply_ranges(const Request &req, Response &res, - std::string &content_type, - std::string &boundary) const { - if (req.ranges.size() > 1) { - auto it = res.headers.find("Content-Type"); - if (it != res.headers.end()) { - content_type = it->second; - res.headers.erase(it); - } - - boundary = detail::make_multipart_data_boundary(); - - res.set_header("Content-Type", - "multipart/byteranges; boundary=" + boundary); - } - - auto type = detail::encoding_type(req, res); - - if (res.body.empty()) { - if (res.content_length_ > 0) { - size_t length = 0; - if (req.ranges.empty()) { - length = res.content_length_; - } else if (req.ranges.size() == 1) { - auto offset_and_length = detail::get_range_offset_and_length( - req.ranges[0], res.content_length_); - - length = offset_and_length.second; - - auto content_range = detail::make_content_range_header_field( - offset_and_length, res.content_length_); - res.set_header("Content-Range", content_range); - } else { - length = detail::get_multipart_ranges_data_length( - req, boundary, content_type, res.content_length_); - } - res.set_header("Content-Length", std::to_string(length)); - } else { - if (res.content_provider_) { - if (res.is_chunked_content_provider_) { - res.set_header("Transfer-Encoding", "chunked"); - if (type == detail::EncodingType::Gzip) { - res.set_header("Content-Encoding", "gzip"); - } else if (type == detail::EncodingType::Brotli) { - res.set_header("Content-Encoding", "br"); - } - } - } - } - } else { - if (req.ranges.empty()) { - ; - } else if (req.ranges.size() == 1) { - auto offset_and_length = - detail::get_range_offset_and_length(req.ranges[0], res.body.size()); - auto offset = offset_and_length.first; - auto length = offset_and_length.second; - - auto content_range = detail::make_content_range_header_field( - offset_and_length, res.body.size()); - res.set_header("Content-Range", content_range); - - assert(offset + length <= res.body.size()); - res.body = res.body.substr(offset, length); - } else { - std::string data; - detail::make_multipart_ranges_data(req, res, boundary, content_type, - res.body.size(), data); - res.body.swap(data); - } - - if (type != detail::EncodingType::None) { - std::unique_ptr compressor; - std::string content_encoding; - - if (type == detail::EncodingType::Gzip) { -#ifdef CPPHTTPLIB_ZLIB_SUPPORT - compressor = detail::make_unique(); - content_encoding = "gzip"; -#endif - } else if (type == detail::EncodingType::Brotli) { -#ifdef CPPHTTPLIB_BROTLI_SUPPORT - compressor = detail::make_unique(); - content_encoding = "br"; -#endif - } - - if (compressor) { - std::string compressed; - if (compressor->compress(res.body.data(), res.body.size(), true, - [&](const char *data, size_t data_len) { - compressed.append(data, data_len); - return true; - })) { - res.body.swap(compressed); - res.set_header("Content-Encoding", content_encoding); - } - } - } - - auto length = std::to_string(res.body.size()); - res.set_header("Content-Length", length); - } -} - -inline bool Server::dispatch_request_for_content_reader( - Request &req, Response &res, ContentReader content_reader, - const HandlersForContentReader &handlers) const { - for (const auto &x : handlers) { - const auto &matcher = x.first; - const auto &handler = x.second; - - if (matcher->match(req)) { - handler(req, res, content_reader); - return true; - } - } - return false; -} - inline bool -Server::process_request(Stream &strm, bool close_connection, - bool &connection_closed, - const std::function &setup_request) { - std::array buf{}; +Server::process_request(Stream &strm, bool last_connection, + bool &connection_close, + std::function setup_request) { + const auto bufsiz = 2048; + char buf[bufsiz]; - detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + detail::stream_line_reader reader(strm, buf, bufsiz); // Connection has been closed on client - if (!line_reader.getline()) { return false; } + if (!reader.getline()) { return false; } Request req; - Response res; - res.version = "HTTP/1.1"; - res.headers = default_headers_; -#ifdef _WIN32 - // TODO: Increase FD_SETSIZE statically (libzmq), dynamically (MySQL). -#else -#ifndef CPPHTTPLIB_USE_POLL - // Socket file descriptor exceeded FD_SETSIZE... - if (strm.socket() >= FD_SETSIZE) { - Headers dummy; - detail::read_headers(strm, dummy); - res.status = StatusCode::InternalServerError_500; - return write_response(strm, close_connection, req, res); - } -#endif -#endif + res.version = "HTTP/1.1"; // Check if the request URI doesn't exceed the limit - if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { - Headers dummy; - detail::read_headers(strm, dummy); - res.status = StatusCode::UriTooLong_414; - return write_response(strm, close_connection, req, res); + if (reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { + res.status = 414; + write_response(strm, last_connection, req, res); + return true; } // Request line and headers - if (!parse_request_line(line_reader.ptr(), req) || + if (!parse_request_line(reader.ptr(), req) || !detail::read_headers(strm, req.headers)) { - res.status = StatusCode::BadRequest_400; - return write_response(strm, close_connection, req, res); + res.status = 400; + write_response(strm, last_connection, req, res); + return true; } if (req.get_header_value("Connection") == "close") { - connection_closed = true; + connection_close = true; } - if (req.version == "HTTP/1.0" && - req.get_header_value("Connection") != "Keep-Alive") { - connection_closed = true; - } + req.set_header("REMOTE_ADDR", strm.get_remote_addr().c_str()); - strm.get_remote_ip_and_port(req.remote_addr, req.remote_port); - req.set_header("REMOTE_ADDR", req.remote_addr); - req.set_header("REMOTE_PORT", std::to_string(req.remote_port)); + // Body + if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") { + if (!detail::read_content( + strm, req, payload_max_length_, res.status, Progress(), + [&](const char *buf, size_t n) { req.body.append(buf, n); })) { + write_response(strm, last_connection, req, res); + return true; + } - strm.get_local_ip_and_port(req.local_addr, req.local_port); - req.set_header("LOCAL_ADDR", req.local_addr); - req.set_header("LOCAL_PORT", std::to_string(req.local_port)); + const auto &content_type = req.get_header_value("Content-Type"); - if (req.has_header("Range")) { - const auto &range_header_value = req.get_header_value("Range"); - if (!detail::parse_range_header(range_header_value, req.ranges)) { - res.status = StatusCode::RangeNotSatisfiable_416; - return write_response(strm, close_connection, req, res); + if (!content_type.find("application/x-www-form-urlencoded")) { + detail::parse_query_text(req.body, req.params); + } else if (!content_type.find("multipart/form-data")) { + std::string boundary; + if (!detail::parse_multipart_boundary(content_type, boundary) || + !detail::parse_multipart_formdata(boundary, req.body, req.files)) { + res.status = 400; + write_response(strm, last_connection, req, res); + return true; + } } } + // TODO: Add additional request info if (setup_request) { setup_request(req); } - if (req.get_header_value("Expect") == "100-continue") { - int status = StatusCode::Continue_100; - if (expect_100_continue_handler_) { - status = expect_100_continue_handler_(req, res); - } - switch (status) { - case StatusCode::Continue_100: - case StatusCode::ExpectationFailed_417: - strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status, - status_message(status)); - break; - default: return write_response(strm, close_connection, req, res); - } - } - - // Routing - auto routed = false; -#ifdef CPPHTTPLIB_NO_EXCEPTIONS - routed = routing(req, res, strm); -#else - try { - routed = routing(req, res, strm); - } catch (std::exception &e) { - if (exception_handler_) { - auto ep = std::current_exception(); - exception_handler_(req, res, ep); - routed = true; - } else { - res.status = StatusCode::InternalServerError_500; - std::string val; - auto s = e.what(); - for (size_t i = 0; s[i]; i++) { - switch (s[i]) { - case '\r': val += "\\r"; break; - case '\n': val += "\\n"; break; - default: val += s[i]; break; - } - } - res.set_header("EXCEPTION_WHAT", val); - } - } catch (...) { - if (exception_handler_) { - auto ep = std::current_exception(); - exception_handler_(req, res, ep); - routed = true; - } else { - res.status = StatusCode::InternalServerError_500; - res.set_header("EXCEPTION_WHAT", "UNKNOWN"); - } - } -#endif - if (routed) { - if (res.status == -1) { - res.status = req.ranges.empty() ? StatusCode::OK_200 - : StatusCode::PartialContent_206; - } - - if (detail::range_error(req, res)) { - res.body.clear(); - res.content_length_ = 0; - res.content_provider_ = nullptr; - res.status = StatusCode::RangeNotSatisfiable_416; - return write_response(strm, close_connection, req, res); - } - - return write_response_with_content(strm, close_connection, req, res); + if (routing(req, res)) { + if (res.status == -1) { res.status = 200; } } else { - if (res.status == -1) { res.status = StatusCode::NotFound_404; } - - return write_response(strm, close_connection, req, res); + res.status = 404; } + + write_response(strm, last_connection, req, res); + return true; } inline bool Server::is_valid() const { return true; } -inline bool Server::process_and_close_socket(socket_t sock) { - auto ret = detail::process_server_socket( - svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_, - read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, - write_timeout_usec_, - [this](Stream &strm, bool close_connection, bool &connection_closed) { - return process_request(strm, close_connection, connection_closed, - nullptr); +inline bool Server::read_and_close_socket(socket_t sock) { + return detail::read_and_close_socket( + sock, keep_alive_max_count_, + [this](Stream &strm, bool last_connection, bool &connection_close) { + return process_request(strm, last_connection, connection_close); }); - - detail::shutdown_socket(sock); - detail::close_socket(sock); - return ret; } // HTTP client implementation -inline ClientImpl::ClientImpl(const std::string &host) - : ClientImpl(host, 80, std::string(), std::string()) {} +inline Client::Client(const char *host, int port, time_t timeout_sec) + : host_(host), port_(port), timeout_sec_(timeout_sec), + host_and_port_(host_ + ":" + std::to_string(port_)) {} -inline ClientImpl::ClientImpl(const std::string &host, int port) - : ClientImpl(host, port, std::string(), std::string()) {} +inline Client::~Client() {} -inline ClientImpl::ClientImpl(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path) - : host_(host), port_(port), - host_and_port_(adjust_host_string(host) + ":" + std::to_string(port)), - client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} +inline bool Client::is_valid() const { return true; } -inline ClientImpl::~ClientImpl() { - std::lock_guard guard(socket_mutex_); - shutdown_socket(socket_); - close_socket(socket_); -} +inline socket_t Client::create_client_socket() const { + return detail::create_socket( + host_.c_str(), port_, [=](socket_t sock, struct addrinfo &ai) -> bool { + detail::set_nonblocking(sock, true); -inline bool ClientImpl::is_valid() const { return true; } - -inline void ClientImpl::copy_settings(const ClientImpl &rhs) { - client_cert_path_ = rhs.client_cert_path_; - client_key_path_ = rhs.client_key_path_; - connection_timeout_sec_ = rhs.connection_timeout_sec_; - read_timeout_sec_ = rhs.read_timeout_sec_; - read_timeout_usec_ = rhs.read_timeout_usec_; - write_timeout_sec_ = rhs.write_timeout_sec_; - write_timeout_usec_ = rhs.write_timeout_usec_; - basic_auth_username_ = rhs.basic_auth_username_; - basic_auth_password_ = rhs.basic_auth_password_; - bearer_token_auth_token_ = rhs.bearer_token_auth_token_; -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - digest_auth_username_ = rhs.digest_auth_username_; - digest_auth_password_ = rhs.digest_auth_password_; -#endif - keep_alive_ = rhs.keep_alive_; - follow_location_ = rhs.follow_location_; - url_encode_ = rhs.url_encode_; - address_family_ = rhs.address_family_; - tcp_nodelay_ = rhs.tcp_nodelay_; - socket_options_ = rhs.socket_options_; - compress_ = rhs.compress_; - decompress_ = rhs.decompress_; - interface_ = rhs.interface_; - proxy_host_ = rhs.proxy_host_; - proxy_port_ = rhs.proxy_port_; - proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_; - proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_; - proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_; -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_; - proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_; -#endif -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - ca_cert_file_path_ = rhs.ca_cert_file_path_; - ca_cert_dir_path_ = rhs.ca_cert_dir_path_; - ca_cert_store_ = rhs.ca_cert_store_; -#endif -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - server_certificate_verification_ = rhs.server_certificate_verification_; -#endif - logger_ = rhs.logger_; -} - -inline socket_t ClientImpl::create_client_socket(Error &error) const { - if (!proxy_host_.empty() && proxy_port_ != -1) { - return detail::create_client_socket( - proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_, - socket_options_, connection_timeout_sec_, connection_timeout_usec_, - read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, - write_timeout_usec_, interface_, error); - } - - // Check is custom IP specified for host_ - std::string ip; - auto it = addr_map_.find(host_); - if (it != addr_map_.end()) { ip = it->second; } - - return detail::create_client_socket( - host_, ip, port_, address_family_, tcp_nodelay_, socket_options_, - connection_timeout_sec_, connection_timeout_usec_, read_timeout_sec_, - read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, interface_, - error); -} - -inline bool ClientImpl::create_and_connect_socket(Socket &socket, - Error &error) { - auto sock = create_client_socket(error); - if (sock == INVALID_SOCKET) { return false; } - socket.sock = sock; - return true; -} - -inline void ClientImpl::shutdown_ssl(Socket & /*socket*/, - bool /*shutdown_gracefully*/) { - // If there are any requests in flight from threads other than us, then it's - // a thread-unsafe race because individual ssl* objects are not thread-safe. - assert(socket_requests_in_flight_ == 0 || - socket_requests_are_from_thread_ == std::this_thread::get_id()); -} - -inline void ClientImpl::shutdown_socket(Socket &socket) const { - if (socket.sock == INVALID_SOCKET) { return; } - detail::shutdown_socket(socket.sock); -} - -inline void ClientImpl::close_socket(Socket &socket) { - // If there are requests in flight in another thread, usually closing - // the socket will be fine and they will simply receive an error when - // using the closed socket, but it is still a bug since rarely the OS - // may reassign the socket id to be used for a new socket, and then - // suddenly they will be operating on a live socket that is different - // than the one they intended! - assert(socket_requests_in_flight_ == 0 || - socket_requests_are_from_thread_ == std::this_thread::get_id()); - - // It is also a bug if this happens while SSL is still active -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - assert(socket.ssl == nullptr); -#endif - if (socket.sock == INVALID_SOCKET) { return; } - detail::close_socket(socket.sock); - socket.sock = INVALID_SOCKET; -} - -inline bool ClientImpl::read_response_line(Stream &strm, const Request &req, - Response &res) const { - std::array buf{}; - - detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); - - if (!line_reader.getline()) { return false; } - -#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR - const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n"); -#else - const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n"); -#endif - - std::cmatch m; - if (!std::regex_match(line_reader.ptr(), m, re)) { - return req.method == "CONNECT"; - } - res.version = std::string(m[1]); - res.status = std::stoi(std::string(m[2])); - res.reason = std::string(m[3]); - - // Ignore '100 Continue' - while (res.status == StatusCode::Continue_100) { - if (!line_reader.getline()) { return false; } // CRLF - if (!line_reader.getline()) { return false; } // next response line - - if (!std::regex_match(line_reader.ptr(), m, re)) { return false; } - res.version = std::string(m[1]); - res.status = std::stoi(std::string(m[2])); - res.reason = std::string(m[3]); - } - - return true; -} - -inline bool ClientImpl::send(Request &req, Response &res, Error &error) { - std::lock_guard request_mutex_guard(request_mutex_); - auto ret = send_(req, res, error); - if (error == Error::SSLPeerCouldBeClosed_) { - assert(!ret); - ret = send_(req, res, error); - } - return ret; -} - -inline bool ClientImpl::send_(Request &req, Response &res, Error &error) { - { - std::lock_guard guard(socket_mutex_); - - // Set this to false immediately - if it ever gets set to true by the end of - // the request, we know another thread instructed us to close the socket. - socket_should_be_closed_when_request_is_done_ = false; - - auto is_alive = false; - if (socket_.is_open()) { - is_alive = detail::is_socket_alive(socket_.sock); - if (!is_alive) { - // Attempt to avoid sigpipe by shutting down nongracefully if it seems - // like the other side has already closed the connection Also, there - // cannot be any requests in flight from other threads since we locked - // request_mutex_, so safe to close everything immediately - const bool shutdown_gracefully = false; - shutdown_ssl(socket_, shutdown_gracefully); - shutdown_socket(socket_); - close_socket(socket_); - } - } - - if (!is_alive) { - if (!create_and_connect_socket(socket_, error)) { return false; } - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - // TODO: refactoring - if (is_ssl()) { - auto &scli = static_cast(*this); - if (!proxy_host_.empty() && proxy_port_ != -1) { - auto success = false; - if (!scli.connect_with_proxy(socket_, res, success, error)) { - return success; + auto ret = connect(sock, ai.ai_addr, static_cast(ai.ai_addrlen)); + if (ret < 0) { + if (detail::is_connection_error() || + !detail::wait_until_socket_is_ready(sock, timeout_sec_, 0)) { + detail::close_socket(sock); + return false; } } - if (!scli.initialize_ssl(socket_, error)) { return false; } - } -#endif - } - - // Mark the current socket as being in use so that it cannot be closed by - // anyone else while this request is ongoing, even though we will be - // releasing the mutex. - if (socket_requests_in_flight_ > 1) { - assert(socket_requests_are_from_thread_ == std::this_thread::get_id()); - } - socket_requests_in_flight_ += 1; - socket_requests_are_from_thread_ = std::this_thread::get_id(); - } - - for (const auto &header : default_headers_) { - if (req.headers.find(header.first) == req.headers.end()) { - req.headers.insert(header); - } - } - - auto ret = false; - auto close_connection = !keep_alive_; - - auto se = detail::scope_exit([&]() { - // Briefly lock mutex in order to mark that a request is no longer ongoing - std::lock_guard guard(socket_mutex_); - socket_requests_in_flight_ -= 1; - if (socket_requests_in_flight_ <= 0) { - assert(socket_requests_in_flight_ == 0); - socket_requests_are_from_thread_ = std::thread::id(); - } - - if (socket_should_be_closed_when_request_is_done_ || close_connection || - !ret) { - shutdown_ssl(socket_, true); - shutdown_socket(socket_); - close_socket(socket_); - } - }); - - ret = process_socket(socket_, [&](Stream &strm) { - return handle_request(strm, req, res, close_connection, error); - }); - - if (!ret) { - if (error == Error::Success) { error = Error::Unknown; } - } - - return ret; + detail::set_nonblocking(sock, false); + return true; + }); } -inline Result ClientImpl::send(const Request &req) { - auto req2 = req; - return send_(std::move(req2)); +inline bool Client::read_response_line(Stream &strm, Response &res) { + const auto bufsiz = 2048; + char buf[bufsiz]; + + detail::stream_line_reader reader(strm, buf, bufsiz); + + if (!reader.getline()) { return false; } + + const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n"); + + std::cmatch m; + if (std::regex_match(reader.ptr(), m, re)) { + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + } + + return true; } -inline Result ClientImpl::send_(Request &&req) { - auto res = detail::make_unique(); - auto error = Error::Success; - auto ret = send(req, *res, error); - return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)}; +inline bool Client::send(Request &req, Response &res) { + if (req.path.empty()) { return false; } + + auto sock = create_client_socket(); + if (sock == INVALID_SOCKET) { return false; } + + return read_and_close_socket(sock, req, res); } -inline bool ClientImpl::handle_request(Stream &strm, Request &req, - Response &res, bool close_connection, - Error &error) { - if (req.path.empty()) { - error = Error::Connection; - return false; - } +inline void Client::write_request(Stream &strm, Request &req) { + BufferStream bstrm; - auto req_save = req; + // Request line + auto path = detail::encode_url(req.path); - bool ret; - - if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) { - auto req2 = req; - req2.path = "http://" + host_and_port_ + req.path; - ret = process_request(strm, req2, res, close_connection, error); - req = req2; - req.path = req_save.path; - } else { - ret = process_request(strm, req, res, close_connection, error); - } - - if (!ret) { return false; } - - if (res.get_header_value("Connection") == "close" || - (res.version == "HTTP/1.0" && res.reason != "Connection established")) { - // TODO this requires a not-entirely-obvious chain of calls to be correct - // for this to be safe. - - // This is safe to call because handle_request is only called by send_ - // which locks the request mutex during the process. It would be a bug - // to call it from a different thread since it's a thread-safety issue - // to do these things to the socket if another thread is using the socket. - std::lock_guard guard(socket_mutex_); - shutdown_ssl(socket_, true); - shutdown_socket(socket_); - close_socket(socket_); - } - - if (300 < res.status && res.status < 400 && follow_location_) { - req = req_save; - ret = redirect(req, res, error); - } - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - if ((res.status == StatusCode::Unauthorized_401 || - res.status == StatusCode::ProxyAuthenticationRequired_407) && - req.authorization_count_ < 5) { - auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407; - const auto &username = - is_proxy ? proxy_digest_auth_username_ : digest_auth_username_; - const auto &password = - is_proxy ? proxy_digest_auth_password_ : digest_auth_password_; - - if (!username.empty() && !password.empty()) { - std::map auth; - if (detail::parse_www_authenticate(res, auth, is_proxy)) { - Request new_req = req; - new_req.authorization_count_ += 1; - new_req.headers.erase(is_proxy ? "Proxy-Authorization" - : "Authorization"); - new_req.headers.insert(detail::make_digest_authentication_header( - req, auth, new_req.authorization_count_, detail::random_string(10), - username, password, is_proxy)); - - Response new_res; - - ret = send(new_req, new_res, error); - if (ret) { res = new_res; } - } - } - } -#endif - - return ret; -} - -inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) { - if (req.redirect_count_ == 0) { - error = Error::ExceedRedirectCount; - return false; - } - - auto location = res.get_header_value("location"); - if (location.empty()) { return false; } - - const static std::regex re( - R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)"); - - std::smatch m; - if (!std::regex_match(location, m, re)) { return false; } - - auto scheme = is_ssl() ? "https" : "http"; - - auto next_scheme = m[1].str(); - auto next_host = m[2].str(); - if (next_host.empty()) { next_host = m[3].str(); } - auto port_str = m[4].str(); - auto next_path = m[5].str(); - auto next_query = m[6].str(); - - auto next_port = port_; - if (!port_str.empty()) { - next_port = std::stoi(port_str); - } else if (!next_scheme.empty()) { - next_port = next_scheme == "https" ? 443 : 80; - } - - if (next_scheme.empty()) { next_scheme = scheme; } - if (next_host.empty()) { next_host = host_; } - if (next_path.empty()) { next_path = "/"; } - - auto path = detail::decode_url(next_path, true) + next_query; - - if (next_scheme == scheme && next_host == host_ && next_port == port_) { - return detail::redirect(*this, req, res, path, location, error); - } else { - if (next_scheme == "https") { -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - SSLClient cli(next_host, next_port); - cli.copy_settings(*this); - if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); } - return detail::redirect(cli, req, res, path, location, error); -#else - return false; -#endif - } else { - ClientImpl cli(next_host, next_port); - cli.copy_settings(*this); - return detail::redirect(cli, req, res, path, location, error); - } - } -} - -inline bool ClientImpl::write_content_with_provider(Stream &strm, - const Request &req, - Error &error) const { - auto is_shutting_down = []() { return false; }; - - if (req.is_chunked_content_provider_) { - // TODO: Brotli support - std::unique_ptr compressor; -#ifdef CPPHTTPLIB_ZLIB_SUPPORT - if (compress_) { - compressor = detail::make_unique(); - } else -#endif - { - compressor = detail::make_unique(); - } - - return detail::write_content_chunked(strm, req.content_provider_, - is_shutting_down, *compressor, error); - } else { - return detail::write_content(strm, req.content_provider_, 0, - req.content_length_, is_shutting_down, error); - } -} - -inline bool ClientImpl::write_request(Stream &strm, Request &req, - bool close_connection, Error &error) { - // Prepare additional headers - if (close_connection) { - if (!req.has_header("Connection")) { - req.set_header("Connection", "close"); - } - } + bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); + // Headers if (!req.has_header("Host")) { if (is_ssl()) { if (port_ == 443) { - req.set_header("Host", host_); + req.set_header("Host", host_.c_str()); } else { - req.set_header("Host", host_and_port_); + req.set_header("Host", host_and_port_.c_str()); } } else { if (port_ == 80) { - req.set_header("Host", host_); + req.set_header("Host", host_.c_str()); } else { - req.set_header("Host", host_and_port_); + req.set_header("Host", host_and_port_.c_str()); } } } if (!req.has_header("Accept")) { req.set_header("Accept", "*/*"); } -#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT if (!req.has_header("User-Agent")) { - auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION; - req.set_header("User-Agent", agent); + req.set_header("User-Agent", "cpp-httplib/0.2"); } -#endif + + // TODO: Support KeepAlive connection + // if (!req.has_header("Connection")) { + req.set_header("Connection", "close"); + // } if (req.body.empty()) { - if (req.content_provider_) { - if (!req.is_chunked_content_provider_) { - if (!req.has_header("Content-Length")) { - auto length = std::to_string(req.content_length_); - req.set_header("Content-Length", length); - } - } - } else { - if (req.method == "POST" || req.method == "PUT" || - req.method == "PATCH") { - req.set_header("Content-Length", "0"); - } + if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") { + req.set_header("Content-Length", "0"); } } else { if (!req.has_header("Content-Type")) { @@ -7260,953 +2145,247 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req, if (!req.has_header("Content-Length")) { auto length = std::to_string(req.body.size()); - req.set_header("Content-Length", length); + req.set_header("Content-Length", length.c_str()); } } - if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) { - if (!req.has_header("Authorization")) { - req.headers.insert(make_basic_authentication_header( - basic_auth_username_, basic_auth_password_, false)); - } - } - - if (!proxy_basic_auth_username_.empty() && - !proxy_basic_auth_password_.empty()) { - if (!req.has_header("Proxy-Authorization")) { - req.headers.insert(make_basic_authentication_header( - proxy_basic_auth_username_, proxy_basic_auth_password_, true)); - } - } - - if (!bearer_token_auth_token_.empty()) { - if (!req.has_header("Authorization")) { - req.headers.insert(make_bearer_token_authentication_header( - bearer_token_auth_token_, false)); - } - } - - if (!proxy_bearer_token_auth_token_.empty()) { - if (!req.has_header("Proxy-Authorization")) { - req.headers.insert(make_bearer_token_authentication_header( - proxy_bearer_token_auth_token_, true)); - } - } - - // Request line and headers - { - detail::BufferStream bstrm; - - const auto &path = url_encode_ ? detail::encode_url(req.path) : req.path; - bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); - - header_writer_(bstrm, req.headers); - - // Flush buffer - auto &data = bstrm.get_buffer(); - if (!detail::write_data(strm, data.data(), data.size())) { - error = Error::Write; - return false; - } - } + detail::write_headers(bstrm, req); // Body - if (req.body.empty()) { - return write_content_with_provider(strm, req, error); - } + if (!req.body.empty()) { bstrm.write(req.body.c_str(), req.body.size()); } - if (!detail::write_data(strm, req.body.data(), req.body.size())) { - error = Error::Write; - return false; - } - - return true; + // Flush buffer + auto &data = bstrm.get_buffer(); + strm.write(data.data(), data.size()); } -inline std::unique_ptr ClientImpl::send_with_content_provider( - Request &req, const char *body, size_t content_length, - ContentProvider content_provider, - ContentProviderWithoutLength content_provider_without_length, - const std::string &content_type, Error &error) { - if (!content_type.empty()) { req.set_header("Content-Type", content_type); } - -#ifdef CPPHTTPLIB_ZLIB_SUPPORT - if (compress_) { req.set_header("Content-Encoding", "gzip"); } -#endif - -#ifdef CPPHTTPLIB_ZLIB_SUPPORT - if (compress_ && !content_provider_without_length) { - // TODO: Brotli support - detail::gzip_compressor compressor; - - if (content_provider) { - auto ok = true; - size_t offset = 0; - DataSink data_sink; - - data_sink.write = [&](const char *data, size_t data_len) -> bool { - if (ok) { - auto last = offset + data_len == content_length; - - auto ret = compressor.compress( - data, data_len, last, - [&](const char *compressed_data, size_t compressed_data_len) { - req.body.append(compressed_data, compressed_data_len); - return true; - }); - - if (ret) { - offset += data_len; - } else { - ok = false; - } - } - return ok; - }; - - while (ok && offset < content_length) { - if (!content_provider(offset, content_length - offset, data_sink)) { - error = Error::Canceled; - return nullptr; - } - } - } else { - if (!compressor.compress(body, content_length, true, - [&](const char *data, size_t data_len) { - req.body.append(data, data_len); - return true; - })) { - error = Error::Compression; - return nullptr; - } - } - } else -#endif - { - if (content_provider) { - req.content_length_ = content_length; - req.content_provider_ = std::move(content_provider); - req.is_chunked_content_provider_ = false; - } else if (content_provider_without_length) { - req.content_length_ = 0; - req.content_provider_ = detail::ContentProviderAdapter( - std::move(content_provider_without_length)); - req.is_chunked_content_provider_ = true; - req.set_header("Transfer-Encoding", "chunked"); - } else { - req.body.assign(body, content_length); - } - } - - auto res = detail::make_unique(); - return send(req, *res, error) ? std::move(res) : nullptr; -} - -inline Result ClientImpl::send_with_content_provider( - const std::string &method, const std::string &path, const Headers &headers, - const char *body, size_t content_length, ContentProvider content_provider, - ContentProviderWithoutLength content_provider_without_length, - const std::string &content_type) { - Request req; - req.method = method; - req.headers = headers; - req.path = path; - - auto error = Error::Success; - - auto res = send_with_content_provider( - req, body, content_length, std::move(content_provider), - std::move(content_provider_without_length), content_type, error); - - return Result{std::move(res), error, std::move(req.headers)}; -} - -inline std::string -ClientImpl::adjust_host_string(const std::string &host) const { - if (host.find(':') != std::string::npos) { return "[" + host + "]"; } - return host; -} - -inline bool ClientImpl::process_request(Stream &strm, Request &req, - Response &res, bool close_connection, - Error &error) { +inline bool Client::process_request(Stream &strm, Request &req, Response &res, + bool &connection_close) { // Send request - if (!write_request(strm, req, close_connection, error)) { return false; } - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - if (is_ssl()) { - auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1; - if (!is_proxy_enabled) { - char buf[1]; - if (SSL_peek(socket_.ssl, buf, 1) == 0 && - SSL_get_error(socket_.ssl, 0) == SSL_ERROR_ZERO_RETURN) { - error = Error::SSLPeerCouldBeClosed_; - return false; - } - } - } -#endif + write_request(strm, req); // Receive response and headers - if (!read_response_line(strm, req, res) || + if (!read_response_line(strm, res) || !detail::read_headers(strm, res.headers)) { - error = Error::Read; return false; } + if (res.get_header_value("Connection") == "close" || + res.version == "HTTP/1.0") { + connection_close = true; + } + // Body - if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" && - req.method != "CONNECT") { - auto redirect = 300 < res.status && res.status < 400 && follow_location_; - - if (req.response_handler && !redirect) { - if (!req.response_handler(res)) { - error = Error::Canceled; - return false; - } - } - - auto out = - req.content_receiver - ? static_cast( - [&](const char *buf, size_t n, uint64_t off, uint64_t len) { - if (redirect) { return true; } - auto ret = req.content_receiver(buf, n, off, len); - if (!ret) { error = Error::Canceled; } - return ret; - }) - : static_cast( - [&](const char *buf, size_t n, uint64_t /*off*/, - uint64_t /*len*/) { - if (res.body.size() + n > res.body.max_size()) { - return false; - } - res.body.append(buf, n); - return true; - }); - - auto progress = [&](uint64_t current, uint64_t total) { - if (!req.progress || redirect) { return true; } - auto ret = req.progress(current, total); - if (!ret) { error = Error::Canceled; } - return ret; + if (req.method != "HEAD") { + ContentReceiver out = [&](const char *buf, size_t n) { + res.body.append(buf, n); }; + if (res.content_receiver) { + out = [&](const char *buf, size_t n) { res.content_receiver(buf, n); }; + } + int dummy_status; - if (!detail::read_content(strm, res, (std::numeric_limits::max)(), - dummy_status, std::move(progress), std::move(out), - decompress_)) { - if (error != Error::Canceled) { error = Error::Read; } + if (!detail::read_content(strm, res, std::numeric_limits::max(), + dummy_status, res.progress, out)) { return false; } } - // Log - if (logger_) { logger_(req, res); } - return true; } -inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider( - const std::string &boundary, const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items) const { - size_t cur_item = 0; - size_t cur_start = 0; - // cur_item and cur_start are copied to within the std::function and maintain - // state between successive calls - return [&, cur_item, cur_start](size_t offset, - DataSink &sink) mutable -> bool { - if (!offset && !items.empty()) { - sink.os << detail::serialize_multipart_formdata(items, boundary, false); - return true; - } else if (cur_item < provider_items.size()) { - if (!cur_start) { - const auto &begin = detail::serialize_multipart_formdata_item_begin( - provider_items[cur_item], boundary); - offset += begin.size(); - cur_start = offset; - sink.os << begin; - } - - DataSink cur_sink; - auto has_data = true; - cur_sink.write = sink.write; - cur_sink.done = [&]() { has_data = false; }; - - if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) { - return false; - } - - if (!has_data) { - sink.os << detail::serialize_multipart_formdata_item_end(); - cur_item++; - cur_start = 0; - } - return true; - } else { - sink.os << detail::serialize_multipart_formdata_finish(boundary); - sink.done(); - return true; - } - }; +inline bool Client::read_and_close_socket(socket_t sock, Request &req, + Response &res) { + return detail::read_and_close_socket( + sock, 0, + [&](Stream &strm, bool /*last_connection*/, bool &connection_close) { + return process_request(strm, req, res, connection_close); + }); } -inline bool -ClientImpl::process_socket(const Socket &socket, - std::function callback) { - return detail::process_client_socket( - socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, - write_timeout_usec_, std::move(callback)); +inline bool Client::is_ssl() const { return false; } + +inline std::shared_ptr Client::Get(const char *path, + Progress progress) { + return Get(path, Headers(), progress); } -inline bool ClientImpl::is_ssl() const { return false; } - -inline Result ClientImpl::Get(const std::string &path) { - return Get(path, Headers(), Progress()); -} - -inline Result ClientImpl::Get(const std::string &path, Progress progress) { - return Get(path, Headers(), std::move(progress)); -} - -inline Result ClientImpl::Get(const std::string &path, const Headers &headers) { - return Get(path, headers, Progress()); -} - -inline Result ClientImpl::Get(const std::string &path, const Headers &headers, - Progress progress) { +inline std::shared_ptr +Client::Get(const char *path, const Headers &headers, Progress progress) { Request req; req.method = "GET"; req.path = path; req.headers = headers; - req.progress = std::move(progress); - return send_(std::move(req)); + auto res = std::make_shared(); + res->progress = progress; + + return send(req, *res) ? res : nullptr; } -inline Result ClientImpl::Get(const std::string &path, - ContentReceiver content_receiver) { - return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr); +inline std::shared_ptr Client::Get(const char *path, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, Headers(), content_receiver, progress); } -inline Result ClientImpl::Get(const std::string &path, - ContentReceiver content_receiver, - Progress progress) { - return Get(path, Headers(), nullptr, std::move(content_receiver), - std::move(progress)); -} - -inline Result ClientImpl::Get(const std::string &path, const Headers &headers, - ContentReceiver content_receiver) { - return Get(path, headers, nullptr, std::move(content_receiver), nullptr); -} - -inline Result ClientImpl::Get(const std::string &path, const Headers &headers, - ContentReceiver content_receiver, - Progress progress) { - return Get(path, headers, nullptr, std::move(content_receiver), - std::move(progress)); -} - -inline Result ClientImpl::Get(const std::string &path, - ResponseHandler response_handler, - ContentReceiver content_receiver) { - return Get(path, Headers(), std::move(response_handler), - std::move(content_receiver), nullptr); -} - -inline Result ClientImpl::Get(const std::string &path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver) { - return Get(path, headers, std::move(response_handler), - std::move(content_receiver), nullptr); -} - -inline Result ClientImpl::Get(const std::string &path, - ResponseHandler response_handler, - ContentReceiver content_receiver, - Progress progress) { - return Get(path, Headers(), std::move(response_handler), - std::move(content_receiver), std::move(progress)); -} - -inline Result ClientImpl::Get(const std::string &path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver, - Progress progress) { +inline std::shared_ptr Client::Get(const char *path, + const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { Request req; req.method = "GET"; req.path = path; req.headers = headers; - req.response_handler = std::move(response_handler); - req.content_receiver = - [content_receiver](const char *data, size_t data_length, - uint64_t /*offset*/, uint64_t /*total_length*/) { - return content_receiver(data, data_length); - }; - req.progress = std::move(progress); - return send_(std::move(req)); + auto res = std::make_shared(); + res->content_receiver = content_receiver; + res->progress = progress; + + return send(req, *res) ? res : nullptr; } -inline Result ClientImpl::Get(const std::string &path, const Params ¶ms, - const Headers &headers, Progress progress) { - if (params.empty()) { return Get(path, headers); } - - std::string path_with_query = append_query_params(path, params); - return Get(path_with_query, headers, std::move(progress)); -} - -inline Result ClientImpl::Get(const std::string &path, const Params ¶ms, - const Headers &headers, - ContentReceiver content_receiver, - Progress progress) { - return Get(path, params, headers, nullptr, std::move(content_receiver), - std::move(progress)); -} - -inline Result ClientImpl::Get(const std::string &path, const Params ¶ms, - const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver, - Progress progress) { - if (params.empty()) { - return Get(path, headers, std::move(response_handler), - std::move(content_receiver), std::move(progress)); - } - - std::string path_with_query = append_query_params(path, params); - return Get(path_with_query, headers, std::move(response_handler), - std::move(content_receiver), std::move(progress)); -} - -inline Result ClientImpl::Head(const std::string &path) { +inline std::shared_ptr Client::Head(const char *path) { return Head(path, Headers()); } -inline Result ClientImpl::Head(const std::string &path, - const Headers &headers) { +inline std::shared_ptr Client::Head(const char *path, + const Headers &headers) { Request req; req.method = "HEAD"; req.headers = headers; req.path = path; - return send_(std::move(req)); + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; } -inline Result ClientImpl::Post(const std::string &path) { - return Post(path, std::string(), std::string()); -} - -inline Result ClientImpl::Post(const std::string &path, - const Headers &headers) { - return Post(path, headers, nullptr, 0, std::string()); -} - -inline Result ClientImpl::Post(const std::string &path, const char *body, - size_t content_length, - const std::string &content_type) { - return Post(path, Headers(), body, content_length, content_type); -} - -inline Result ClientImpl::Post(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type) { - return send_with_content_provider("POST", path, headers, body, content_length, - nullptr, nullptr, content_type); -} - -inline Result ClientImpl::Post(const std::string &path, const std::string &body, - const std::string &content_type) { +inline std::shared_ptr Client::Post(const char *path, + const std::string &body, + const char *content_type) { return Post(path, Headers(), body, content_type); } -inline Result ClientImpl::Post(const std::string &path, const Headers &headers, - const std::string &body, - const std::string &content_type) { - return send_with_content_provider("POST", path, headers, body.data(), - body.size(), nullptr, nullptr, - content_type); +inline std::shared_ptr Client::Post(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + Request req; + req.method = "POST"; + req.headers = headers; + req.path = path; + + req.headers.emplace("Content-Type", content_type); + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; } -inline Result ClientImpl::Post(const std::string &path, const Params ¶ms) { +inline std::shared_ptr Client::Post(const char *path, + const Params ¶ms) { return Post(path, Headers(), params); } -inline Result ClientImpl::Post(const std::string &path, size_t content_length, - ContentProvider content_provider, - const std::string &content_type) { - return Post(path, Headers(), content_length, std::move(content_provider), - content_type); -} +inline std::shared_ptr +Client::Post(const char *path, const Headers &headers, const Params ¶ms) { + std::string query; + for (auto it = params.begin(); it != params.end(); ++it) { + if (it != params.begin()) { query += "&"; } + query += it->first; + query += "="; + query += detail::encode_url(it->second); + } -inline Result ClientImpl::Post(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type) { - return Post(path, Headers(), std::move(content_provider), content_type); -} - -inline Result ClientImpl::Post(const std::string &path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const std::string &content_type) { - return send_with_content_provider("POST", path, headers, nullptr, - content_length, std::move(content_provider), - nullptr, content_type); -} - -inline Result ClientImpl::Post(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type) { - return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr, - std::move(content_provider), content_type); -} - -inline Result ClientImpl::Post(const std::string &path, const Headers &headers, - const Params ¶ms) { - auto query = detail::params_to_query_str(params); return Post(path, headers, query, "application/x-www-form-urlencoded"); } -inline Result ClientImpl::Post(const std::string &path, - const MultipartFormDataItems &items) { - return Post(path, Headers(), items); -} - -inline Result ClientImpl::Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items) { - const auto &boundary = detail::make_multipart_data_boundary(); - const auto &content_type = - detail::serialize_multipart_formdata_get_content_type(boundary); - const auto &body = detail::serialize_multipart_formdata(items, boundary); - return Post(path, headers, body, content_type); -} - -inline Result ClientImpl::Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const std::string &boundary) { - if (!detail::is_multipart_boundary_chars_valid(boundary)) { - return Result{nullptr, Error::UnsupportedMultipartBoundaryChars}; - } - - const auto &content_type = - detail::serialize_multipart_formdata_get_content_type(boundary); - const auto &body = detail::serialize_multipart_formdata(items, boundary); - return Post(path, headers, body, content_type); -} - -inline Result -ClientImpl::Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items) { - const auto &boundary = detail::make_multipart_data_boundary(); - const auto &content_type = - detail::serialize_multipart_formdata_get_content_type(boundary); - return send_with_content_provider( - "POST", path, headers, nullptr, 0, nullptr, - get_multipart_content_provider(boundary, items, provider_items), - content_type); -} - -inline Result ClientImpl::Put(const std::string &path) { - return Put(path, std::string(), std::string()); -} - -inline Result ClientImpl::Put(const std::string &path, const char *body, - size_t content_length, - const std::string &content_type) { - return Put(path, Headers(), body, content_length, content_type); -} - -inline Result ClientImpl::Put(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type) { - return send_with_content_provider("PUT", path, headers, body, content_length, - nullptr, nullptr, content_type); -} - -inline Result ClientImpl::Put(const std::string &path, const std::string &body, - const std::string &content_type) { +inline std::shared_ptr Client::Put(const char *path, + const std::string &body, + const char *content_type) { return Put(path, Headers(), body, content_type); } -inline Result ClientImpl::Put(const std::string &path, const Headers &headers, - const std::string &body, - const std::string &content_type) { - return send_with_content_provider("PUT", path, headers, body.data(), - body.size(), nullptr, nullptr, - content_type); +inline std::shared_ptr Client::Put(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + Request req; + req.method = "PUT"; + req.headers = headers; + req.path = path; + + req.headers.emplace("Content-Type", content_type); + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; } -inline Result ClientImpl::Put(const std::string &path, size_t content_length, - ContentProvider content_provider, - const std::string &content_type) { - return Put(path, Headers(), content_length, std::move(content_provider), - content_type); -} - -inline Result ClientImpl::Put(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type) { - return Put(path, Headers(), std::move(content_provider), content_type); -} - -inline Result ClientImpl::Put(const std::string &path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const std::string &content_type) { - return send_with_content_provider("PUT", path, headers, nullptr, - content_length, std::move(content_provider), - nullptr, content_type); -} - -inline Result ClientImpl::Put(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type) { - return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr, - std::move(content_provider), content_type); -} - -inline Result ClientImpl::Put(const std::string &path, const Params ¶ms) { - return Put(path, Headers(), params); -} - -inline Result ClientImpl::Put(const std::string &path, const Headers &headers, - const Params ¶ms) { - auto query = detail::params_to_query_str(params); - return Put(path, headers, query, "application/x-www-form-urlencoded"); -} - -inline Result ClientImpl::Put(const std::string &path, - const MultipartFormDataItems &items) { - return Put(path, Headers(), items); -} - -inline Result ClientImpl::Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items) { - const auto &boundary = detail::make_multipart_data_boundary(); - const auto &content_type = - detail::serialize_multipart_formdata_get_content_type(boundary); - const auto &body = detail::serialize_multipart_formdata(items, boundary); - return Put(path, headers, body, content_type); -} - -inline Result ClientImpl::Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const std::string &boundary) { - if (!detail::is_multipart_boundary_chars_valid(boundary)) { - return Result{nullptr, Error::UnsupportedMultipartBoundaryChars}; - } - - const auto &content_type = - detail::serialize_multipart_formdata_get_content_type(boundary); - const auto &body = detail::serialize_multipart_formdata(items, boundary); - return Put(path, headers, body, content_type); -} - -inline Result -ClientImpl::Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items) { - const auto &boundary = detail::make_multipart_data_boundary(); - const auto &content_type = - detail::serialize_multipart_formdata_get_content_type(boundary); - return send_with_content_provider( - "PUT", path, headers, nullptr, 0, nullptr, - get_multipart_content_provider(boundary, items, provider_items), - content_type); -} -inline Result ClientImpl::Patch(const std::string &path) { - return Patch(path, std::string(), std::string()); -} - -inline Result ClientImpl::Patch(const std::string &path, const char *body, - size_t content_length, - const std::string &content_type) { - return Patch(path, Headers(), body, content_length, content_type); -} - -inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type) { - return send_with_content_provider("PATCH", path, headers, body, - content_length, nullptr, nullptr, - content_type); -} - -inline Result ClientImpl::Patch(const std::string &path, - const std::string &body, - const std::string &content_type) { +inline std::shared_ptr Client::Patch(const char *path, + const std::string &body, + const char *content_type) { return Patch(path, Headers(), body, content_type); } -inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, - const std::string &body, - const std::string &content_type) { - return send_with_content_provider("PATCH", path, headers, body.data(), - body.size(), nullptr, nullptr, - content_type); +inline std::shared_ptr Client::Patch(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + Request req; + req.method = "PATCH"; + req.headers = headers; + req.path = path; + + req.headers.emplace("Content-Type", content_type); + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; } -inline Result ClientImpl::Patch(const std::string &path, size_t content_length, - ContentProvider content_provider, - const std::string &content_type) { - return Patch(path, Headers(), content_length, std::move(content_provider), - content_type); +inline std::shared_ptr Client::Delete(const char *path, + const std::string &body, + const char *content_type) { + return Delete(path, Headers(), body, content_type); } -inline Result ClientImpl::Patch(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type) { - return Patch(path, Headers(), std::move(content_provider), content_type); -} - -inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const std::string &content_type) { - return send_with_content_provider("PATCH", path, headers, nullptr, - content_length, std::move(content_provider), - nullptr, content_type); -} - -inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type) { - return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr, - std::move(content_provider), content_type); -} - -inline Result ClientImpl::Delete(const std::string &path) { - return Delete(path, Headers(), std::string(), std::string()); -} - -inline Result ClientImpl::Delete(const std::string &path, - const Headers &headers) { - return Delete(path, headers, std::string(), std::string()); -} - -inline Result ClientImpl::Delete(const std::string &path, const char *body, - size_t content_length, - const std::string &content_type) { - return Delete(path, Headers(), body, content_length, content_type); -} - -inline Result ClientImpl::Delete(const std::string &path, - const Headers &headers, const char *body, - size_t content_length, - const std::string &content_type) { +inline std::shared_ptr Client::Delete(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { Request req; req.method = "DELETE"; req.headers = headers; req.path = path; - if (!content_type.empty()) { req.set_header("Content-Type", content_type); } - req.body.assign(body, content_length); + if (content_type) { req.headers.emplace("Content-Type", content_type); } + req.body = body; - return send_(std::move(req)); + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; } -inline Result ClientImpl::Delete(const std::string &path, - const std::string &body, - const std::string &content_type) { - return Delete(path, Headers(), body.data(), body.size(), content_type); -} - -inline Result ClientImpl::Delete(const std::string &path, - const Headers &headers, - const std::string &body, - const std::string &content_type) { - return Delete(path, headers, body.data(), body.size(), content_type); -} - -inline Result ClientImpl::Options(const std::string &path) { +inline std::shared_ptr Client::Options(const char *path) { return Options(path, Headers()); } -inline Result ClientImpl::Options(const std::string &path, - const Headers &headers) { +inline std::shared_ptr Client::Options(const char *path, + const Headers &headers) { Request req; req.method = "OPTIONS"; - req.headers = headers; req.path = path; + req.headers = headers; - return send_(std::move(req)); -} + auto res = std::make_shared(); -inline void ClientImpl::stop() { - std::lock_guard guard(socket_mutex_); - - // If there is anything ongoing right now, the ONLY thread-safe thing we can - // do is to shutdown_socket, so that threads using this socket suddenly - // discover they can't read/write any more and error out. Everything else - // (closing the socket, shutting ssl down) is unsafe because these actions are - // not thread-safe. - if (socket_requests_in_flight_ > 0) { - shutdown_socket(socket_); - - // Aside from that, we set a flag for the socket to be closed when we're - // done. - socket_should_be_closed_when_request_is_done_ = true; - return; - } - - // Otherwise, still holding the mutex, we can shut everything down ourselves - shutdown_ssl(socket_, true); - shutdown_socket(socket_); - close_socket(socket_); -} - -inline std::string ClientImpl::host() const { return host_; } - -inline int ClientImpl::port() const { return port_; } - -inline size_t ClientImpl::is_socket_open() const { - std::lock_guard guard(socket_mutex_); - return socket_.is_open(); -} - -inline socket_t ClientImpl::socket() const { return socket_.sock; } - -inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) { - connection_timeout_sec_ = sec; - connection_timeout_usec_ = usec; -} - -inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) { - read_timeout_sec_ = sec; - read_timeout_usec_ = usec; -} - -inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) { - write_timeout_sec_ = sec; - write_timeout_usec_ = usec; -} - -inline void ClientImpl::set_basic_auth(const std::string &username, - const std::string &password) { - basic_auth_username_ = username; - basic_auth_password_ = password; -} - -inline void ClientImpl::set_bearer_token_auth(const std::string &token) { - bearer_token_auth_token_ = token; -} - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -inline void ClientImpl::set_digest_auth(const std::string &username, - const std::string &password) { - digest_auth_username_ = username; - digest_auth_password_ = password; -} -#endif - -inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; } - -inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; } - -inline void ClientImpl::set_url_encode(bool on) { url_encode_ = on; } - -inline void -ClientImpl::set_hostname_addr_map(std::map addr_map) { - addr_map_ = std::move(addr_map); -} - -inline void ClientImpl::set_default_headers(Headers headers) { - default_headers_ = std::move(headers); -} - -inline void ClientImpl::set_header_writer( - std::function const &writer) { - header_writer_ = writer; -} - -inline void ClientImpl::set_address_family(int family) { - address_family_ = family; -} - -inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } - -inline void ClientImpl::set_socket_options(SocketOptions socket_options) { - socket_options_ = std::move(socket_options); -} - -inline void ClientImpl::set_compress(bool on) { compress_ = on; } - -inline void ClientImpl::set_decompress(bool on) { decompress_ = on; } - -inline void ClientImpl::set_interface(const std::string &intf) { - interface_ = intf; -} - -inline void ClientImpl::set_proxy(const std::string &host, int port) { - proxy_host_ = host; - proxy_port_ = port; -} - -inline void ClientImpl::set_proxy_basic_auth(const std::string &username, - const std::string &password) { - proxy_basic_auth_username_ = username; - proxy_basic_auth_password_ = password; -} - -inline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) { - proxy_bearer_token_auth_token_ = token; -} - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -inline void ClientImpl::set_proxy_digest_auth(const std::string &username, - const std::string &password) { - proxy_digest_auth_username_ = username; - proxy_digest_auth_password_ = password; -} - -inline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path, - const std::string &ca_cert_dir_path) { - ca_cert_file_path_ = ca_cert_file_path; - ca_cert_dir_path_ = ca_cert_dir_path; -} - -inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) { - if (ca_cert_store && ca_cert_store != ca_cert_store_) { - ca_cert_store_ = ca_cert_store; - } -} - -inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert, - std::size_t size) const { - auto mem = BIO_new_mem_buf(ca_cert, static_cast(size)); - if (!mem) { return nullptr; } - - auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr); - if (!inf) { - BIO_free_all(mem); - return nullptr; - } - - auto cts = X509_STORE_new(); - if (cts) { - for (auto i = 0; i < static_cast(sk_X509_INFO_num(inf)); i++) { - auto itmp = sk_X509_INFO_value(inf, i); - if (!itmp) { continue; } - - if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); } - if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); } - } - } - - sk_X509_INFO_pop_free(inf, X509_INFO_free); - BIO_free_all(mem); - return cts; -} - -inline void ClientImpl::enable_server_certificate_verification(bool enabled) { - server_certificate_verification_ = enabled; -} -#endif - -inline void ClientImpl::set_logger(Logger logger) { - logger_ = std::move(logger); + return send(req, *res) ? res : nullptr; } /* @@ -8215,227 +2394,129 @@ inline void ClientImpl::set_logger(Logger logger) { #ifdef CPPHTTPLIB_OPENSSL_SUPPORT namespace detail { -template -inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, - U SSL_connect_or_accept, V setup) { +template +inline bool +read_and_close_socket_ssl(socket_t sock, size_t keep_alive_max_count, + // TODO: OpenSSL 1.0.2 occasionally crashes... + // The upcoming 1.1.0 is going to be thread safe. + SSL_CTX *ctx, std::mutex &ctx_mutex, + U SSL_connect_or_accept, V setup, T callback) { SSL *ssl = nullptr; { std::lock_guard guard(ctx_mutex); ssl = SSL_new(ctx); } - if (ssl) { - set_nonblocking(sock, true); - auto bio = BIO_new_socket(static_cast(sock), BIO_NOCLOSE); - BIO_set_nbio(bio, 1); - SSL_set_bio(ssl, bio, bio); - - if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) { - SSL_shutdown(ssl); - { - std::lock_guard guard(ctx_mutex); - SSL_free(ssl); - } - set_nonblocking(sock, false); - return nullptr; - } - BIO_set_nbio(bio, 0); - set_nonblocking(sock, false); - } - - return ssl; -} - -inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, - bool shutdown_gracefully) { - // sometimes we may want to skip this to try to avoid SIGPIPE if we know - // the remote has closed the network connection - // Note that it is not always possible to avoid SIGPIPE, this is merely a - // best-efforts. - if (shutdown_gracefully) { SSL_shutdown(ssl); } - - std::lock_guard guard(ctx_mutex); - SSL_free(ssl); -} - -template -bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl, - U ssl_connect_or_accept, - time_t timeout_sec, - time_t timeout_usec) { - auto res = 0; - while ((res = ssl_connect_or_accept(ssl)) != 1) { - auto err = SSL_get_error(ssl, res); - switch (err) { - case SSL_ERROR_WANT_READ: - if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; } - break; - case SSL_ERROR_WANT_WRITE: - if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; } - break; - default: break; - } + if (!ssl) { + close_socket(sock); return false; } - return true; -} -template -inline bool process_server_socket_ssl( - const std::atomic &svr_sock, SSL *ssl, socket_t sock, - size_t keep_alive_max_count, time_t keep_alive_timeout_sec, - time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, - time_t write_timeout_usec, T callback) { - return process_server_socket_core( - svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec, - [&](bool close_connection, bool &connection_closed) { - SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, - write_timeout_sec, write_timeout_usec); - return callback(strm, close_connection, connection_closed); - }); -} + auto bio = BIO_new_socket(sock, BIO_NOCLOSE); + SSL_set_bio(ssl, bio, bio); -template -inline bool -process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec, - time_t read_timeout_usec, time_t write_timeout_sec, - time_t write_timeout_usec, T callback) { - SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, - write_timeout_sec, write_timeout_usec); - return callback(strm); + if (!setup(ssl)) { + SSL_shutdown(ssl); + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } + + close_socket(sock); + return false; + } + + bool ret = false; + + if (SSL_connect_or_accept(ssl) == 1) { + if (keep_alive_max_count > 0) { + auto count = keep_alive_max_count; + while (count > 0 && + detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { + SSLSocketStream strm(sock, ssl); + auto last_connection = count == 1; + auto connection_close = false; + + ret = callback(ssl, strm, last_connection, connection_close); + if (!ret || connection_close) { break; } + + count--; + } + } else { + SSLSocketStream strm(sock, ssl); + auto dummy_connection_close = false; + ret = callback(ssl, strm, true, dummy_connection_close); + } + } + + SSL_shutdown(ssl); + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } + + close_socket(sock); + + return ret; } class SSLInit { public: SSLInit() { - OPENSSL_init_ssl( - OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); + SSL_load_error_strings(); + SSL_library_init(); } + + ~SSLInit() { ERR_free_strings(); } }; -// SSL socket stream implementation -inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, - time_t read_timeout_sec, - time_t read_timeout_usec, - time_t write_timeout_sec, - time_t write_timeout_usec) - : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec), - read_timeout_usec_(read_timeout_usec), - write_timeout_sec_(write_timeout_sec), - write_timeout_usec_(write_timeout_usec) { - SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY); -} - -inline SSLSocketStream::~SSLSocketStream() = default; - -inline bool SSLSocketStream::is_readable() const { - return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; -} - -inline bool SSLSocketStream::is_writable() const { - return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && - is_socket_alive(sock_); -} - -inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { - if (SSL_pending(ssl_) > 0) { - return SSL_read(ssl_, ptr, static_cast(size)); - } else if (is_readable()) { - auto ret = SSL_read(ssl_, ptr, static_cast(size)); - if (ret < 0) { - auto err = SSL_get_error(ssl_, ret); - auto n = 1000; -#ifdef _WIN32 - while (--n >= 0 && (err == SSL_ERROR_WANT_READ || - (err == SSL_ERROR_SYSCALL && - WSAGetLastError() == WSAETIMEDOUT))) { -#else - while (--n >= 0 && err == SSL_ERROR_WANT_READ) { -#endif - if (SSL_pending(ssl_) > 0) { - return SSL_read(ssl_, ptr, static_cast(size)); - } else if (is_readable()) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - ret = SSL_read(ssl_, ptr, static_cast(size)); - if (ret >= 0) { return ret; } - err = SSL_get_error(ssl_, ret); - } else { - return -1; - } - } - } - return ret; - } - return -1; -} - -inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) { - if (is_writable()) { - auto handle_size = static_cast( - std::min(size, (std::numeric_limits::max)())); - - auto ret = SSL_write(ssl_, ptr, static_cast(handle_size)); - if (ret < 0) { - auto err = SSL_get_error(ssl_, ret); - auto n = 1000; -#ifdef _WIN32 - while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE || - (err == SSL_ERROR_SYSCALL && - WSAGetLastError() == WSAETIMEDOUT))) { -#else - while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) { -#endif - if (is_writable()) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - ret = SSL_write(ssl_, ptr, static_cast(handle_size)); - if (ret >= 0) { return ret; } - err = SSL_get_error(ssl_, ret); - } else { - return -1; - } - } - } - return ret; - } - return -1; -} - -inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip, - int &port) const { - detail::get_remote_ip_and_port(sock_, ip, port); -} - -inline void SSLSocketStream::get_local_ip_and_port(std::string &ip, - int &port) const { - detail::get_local_ip_and_port(sock_, ip, port); -} - -inline socket_t SSLSocketStream::socket() const { return sock_; } - static SSLInit sslinit_; } // namespace detail +// SSL socket stream implementation +inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl) + : sock_(sock), ssl_(ssl) {} + +inline SSLSocketStream::~SSLSocketStream() {} + +inline int SSLSocketStream::read(char *ptr, size_t size) { + if (SSL_pending(ssl_) > 0 || + detail::select_read(sock_, CPPHTTPLIB_READ_TIMEOUT_SECOND, + CPPHTTPLIB_READ_TIMEOUT_USECOND) > 0) { + return SSL_read(ssl_, ptr, size); + } + return -1; +} + +inline int SSLSocketStream::write(const char *ptr, size_t size) { + return SSL_write(ssl_, ptr, size); +} + +inline int SSLSocketStream::write(const char *ptr) { + return write(ptr, strlen(ptr)); +} + +inline std::string SSLSocketStream::get_remote_addr() const { + return detail::get_remote_addr(sock_); +} + // SSL HTTP server implementation inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, const char *client_ca_cert_file_path, - const char *client_ca_cert_dir_path, - const char *private_key_password) { - ctx_ = SSL_CTX_new(TLS_server_method()); + const char *client_ca_cert_dir_path) { + ctx_ = SSL_CTX_new(SSLv23_server_method()); if (ctx_) { SSL_CTX_set_options(ctx_, - SSL_OP_NO_COMPRESSION | + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION); - - // add default password callback before opening encrypted private key - if (private_key_password != nullptr && (private_key_password[0] != '\0')) { - SSL_CTX_set_default_passwd_cb_userdata( - ctx_, - reinterpret_cast(const_cast(private_key_password))); - } + // auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + // SSL_CTX_set_tmp_ecdh(ctx_, ecdh); + // EC_KEY_free(ecdh); if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 || SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != @@ -8443,46 +2524,19 @@ inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, SSL_CTX_free(ctx_); ctx_ = nullptr; } else if (client_ca_cert_file_path || client_ca_cert_dir_path) { + // if (client_ca_cert_file_path) { + // auto list = SSL_load_client_CA_file(client_ca_cert_file_path); + // SSL_CTX_set_client_CA_list(ctx_, list); + // } + SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path, client_ca_cert_dir_path); SSL_CTX_set_verify( - ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); - } - } -} - -inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key, - X509_STORE *client_ca_cert_store) { - ctx_ = SSL_CTX_new(TLS_server_method()); - - if (ctx_) { - SSL_CTX_set_options(ctx_, - SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - - SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION); - - if (SSL_CTX_use_certificate(ctx_, cert) != 1 || - SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) { - SSL_CTX_free(ctx_); - ctx_ = nullptr; - } else if (client_ca_cert_store) { - SSL_CTX_set_cert_store(ctx_, client_ca_cert_store); - - SSL_CTX_set_verify( - ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); - } - } -} - -inline SSLServer::SSLServer( - const std::function &setup_ssl_ctx_callback) { - ctx_ = SSL_CTX_new(TLS_method()); - if (ctx_) { - if (!setup_ssl_ctx_callback(*ctx_)) { - SSL_CTX_free(ctx_); - ctx_ = nullptr; + ctx_, + SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE, + nullptr); } } } @@ -8493,82 +2547,33 @@ inline SSLServer::~SSLServer() { inline bool SSLServer::is_valid() const { return ctx_; } -inline SSL_CTX *SSLServer::ssl_context() const { return ctx_; } - -inline bool SSLServer::process_and_close_socket(socket_t sock) { - auto ssl = detail::ssl_new( - sock, ctx_, ctx_mutex_, - [&](SSL *ssl2) { - return detail::ssl_connect_or_accept_nonblocking( - sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_); - }, - [](SSL * /*ssl2*/) { return true; }); - - auto ret = false; - if (ssl) { - ret = detail::process_server_socket_ssl( - svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_, - read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, - write_timeout_usec_, - [this, ssl](Stream &strm, bool close_connection, - bool &connection_closed) { - return process_request(strm, close_connection, connection_closed, - [&](Request &req) { req.ssl = ssl; }); - }); - - // Shutdown gracefully if the result seemed successful, non-gracefully if - // the connection appeared to be closed. - const bool shutdown_gracefully = ret; - detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully); - } - - detail::shutdown_socket(sock); - detail::close_socket(sock); - return ret; +inline bool SSLServer::read_and_close_socket(socket_t sock) { + return detail::read_and_close_socket_ssl( + sock, keep_alive_max_count_, ctx_, ctx_mutex_, SSL_accept, + [](SSL * /*ssl*/) { return true; }, + [this](SSL *ssl, Stream &strm, bool last_connection, + bool &connection_close) { + return process_request(strm, last_connection, connection_close, + [&](Request &req) { req.ssl = ssl; }); + }); } // SSL HTTP client implementation -inline SSLClient::SSLClient(const std::string &host) - : SSLClient(host, 443, std::string(), std::string()) {} - -inline SSLClient::SSLClient(const std::string &host, int port) - : SSLClient(host, port, std::string(), std::string()) {} - -inline SSLClient::SSLClient(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path) - : ClientImpl(host, port, client_cert_path, client_key_path) { - ctx_ = SSL_CTX_new(TLS_client_method()); +inline SSLClient::SSLClient(const char *host, int port, time_t timeout_sec, + const char *client_cert_path, + const char *client_key_path) + : Client(host, port, timeout_sec) { + ctx_ = SSL_CTX_new(SSLv23_client_method()); detail::split(&host_[0], &host_[host_.size()], '.', [&](const char *b, const char *e) { - host_components_.emplace_back(b, e); + host_components_.emplace_back(std::string(b, e)); }); - - if (!client_cert_path.empty() && !client_key_path.empty()) { - if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), + if (client_cert_path && client_key_path) { + if (SSL_CTX_use_certificate_file(ctx_, client_cert_path, SSL_FILETYPE_PEM) != 1 || - SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), - SSL_FILETYPE_PEM) != 1) { - SSL_CTX_free(ctx_); - ctx_ = nullptr; - } - } -} - -inline SSLClient::SSLClient(const std::string &host, int port, - X509 *client_cert, EVP_PKEY *client_key) - : ClientImpl(host, port) { - ctx_ = SSL_CTX_new(TLS_client_method()); - - detail::split(&host_[0], &host_[host_.size()], '.', - [&](const char *b, const char *e) { - host_components_.emplace_back(b, e); - }); - - if (client_cert != nullptr && client_key != nullptr) { - if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || - SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) { + SSL_CTX_use_PrivateKey_file(ctx_, client_key_path, SSL_FILETYPE_PEM) != + 1) { SSL_CTX_free(ctx_); ctx_ = nullptr; } @@ -8577,230 +2582,69 @@ inline SSLClient::SSLClient(const std::string &host, int port, inline SSLClient::~SSLClient() { if (ctx_) { SSL_CTX_free(ctx_); } - // Make sure to shut down SSL since shutdown_ssl will resolve to the - // base function rather than the derived function once we get to the - // base class destructor, and won't free the SSL (causing a leak). - shutdown_ssl_impl(socket_, true); } inline bool SSLClient::is_valid() const { return ctx_; } -inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) { - if (ca_cert_store) { - if (ctx_) { - if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) { - // Free memory allocated for old cert and use new store `ca_cert_store` - SSL_CTX_set_cert_store(ctx_, ca_cert_store); - } - } else { - X509_STORE_free(ca_cert_store); - } - } +inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path, + const char *ca_cert_dir_path) { + if (ca_cert_file_path) { ca_cert_file_path_ = ca_cert_file_path; } + if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; } } -inline void SSLClient::load_ca_cert_store(const char *ca_cert, - std::size_t size) { - set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size)); +inline void SSLClient::enable_server_certificate_verification(bool enabled) { + server_certificate_verification_ = enabled; } inline long SSLClient::get_openssl_verify_result() const { return verify_result_; } -inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; } +inline bool SSLClient::read_and_close_socket(socket_t sock, Request &req, + Response &res) { -inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) { - return is_valid() && ClientImpl::create_and_connect_socket(socket, error); -} + return is_valid() && + detail::read_and_close_socket_ssl( + sock, 0, ctx_, ctx_mutex_, + [&](SSL *ssl) { + if (ca_cert_file_path_.empty()) { + SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr); + } else { + if (!SSL_CTX_load_verify_locations( + ctx_, ca_cert_file_path_.c_str(), nullptr)) { + return false; + } + SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr); + } -// Assumes that socket_mutex_ is locked and that there are no requests in flight -inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, - bool &success, Error &error) { - success = true; - Response proxy_res; - if (!detail::process_client_socket( - socket.sock, read_timeout_sec_, read_timeout_usec_, - write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { - Request req2; - req2.method = "CONNECT"; - req2.path = host_and_port_; - return process_request(strm, req2, proxy_res, false, error); - })) { - // Thread-safe to close everything because we are assuming there are no - // requests in flight - shutdown_ssl(socket, true); - shutdown_socket(socket); - close_socket(socket); - success = false; - return false; - } + if (SSL_connect(ssl) != 1) { return false; } - if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) { - if (!proxy_digest_auth_username_.empty() && - !proxy_digest_auth_password_.empty()) { - std::map auth; - if (detail::parse_www_authenticate(proxy_res, auth, true)) { - proxy_res = Response(); - if (!detail::process_client_socket( - socket.sock, read_timeout_sec_, read_timeout_usec_, - write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { - Request req3; - req3.method = "CONNECT"; - req3.path = host_and_port_; - req3.headers.insert(detail::make_digest_authentication_header( - req3, auth, 1, detail::random_string(10), - proxy_digest_auth_username_, proxy_digest_auth_password_, - true)); - return process_request(strm, req3, proxy_res, false, error); - })) { - // Thread-safe to close everything because we are assuming there are - // no requests in flight - shutdown_ssl(socket, true); - shutdown_socket(socket); - close_socket(socket); - success = false; - return false; - } - } - } - } + if (server_certificate_verification_) { + verify_result_ = SSL_get_verify_result(ssl); - // If status code is not 200, proxy request is failed. - // Set error to ProxyConnection and return proxy response - // as the response of the request - if (proxy_res.status != StatusCode::OK_200) { - error = Error::ProxyConnection; - res = std::move(proxy_res); - // Thread-safe to close everything because we are assuming there are - // no requests in flight - shutdown_ssl(socket, true); - shutdown_socket(socket); - close_socket(socket); - return false; - } + if (verify_result_ != X509_V_OK) { return false; } - return true; -} + auto server_cert = SSL_get_peer_certificate(ssl); -inline bool SSLClient::load_certs() { - auto ret = true; + if (server_cert == nullptr) { return false; } - std::call_once(initialize_cert_, [&]() { - std::lock_guard guard(ctx_mutex_); - if (!ca_cert_file_path_.empty()) { - if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(), - nullptr)) { - ret = false; - } - } else if (!ca_cert_dir_path_.empty()) { - if (!SSL_CTX_load_verify_locations(ctx_, nullptr, - ca_cert_dir_path_.c_str())) { - ret = false; - } - } else { - auto loaded = false; -#ifdef _WIN32 - loaded = - detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_)); -#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__) -#if TARGET_OS_OSX - loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_)); -#endif // TARGET_OS_OSX -#endif // _WIN32 - if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); } - } - }); + if (!verify_host(server_cert)) { + X509_free(server_cert); + return false; + } + X509_free(server_cert); + } - return ret; -} - -inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) { - auto ssl = detail::ssl_new( - socket.sock, ctx_, ctx_mutex_, - [&](SSL *ssl2) { - if (server_certificate_verification_) { - if (!load_certs()) { - error = Error::SSLLoadingCerts; - return false; - } - SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr); - } - - if (!detail::ssl_connect_or_accept_nonblocking( - socket.sock, ssl2, SSL_connect, connection_timeout_sec_, - connection_timeout_usec_)) { - error = Error::SSLConnection; - return false; - } - - if (server_certificate_verification_) { - verify_result_ = SSL_get_verify_result(ssl2); - - if (verify_result_ != X509_V_OK) { - error = Error::SSLServerVerification; - return false; - } - - auto server_cert = SSL_get1_peer_certificate(ssl2); - - if (server_cert == nullptr) { - error = Error::SSLServerVerification; - return false; - } - - if (!verify_host(server_cert)) { - X509_free(server_cert); - error = Error::SSLServerVerification; - return false; - } - X509_free(server_cert); - } - - return true; - }, - [&](SSL *ssl2) { - // NOTE: Direct call instead of using the OpenSSL macro to suppress - // -Wold-style-cast warning - // SSL_set_tlsext_host_name(ssl2, host_.c_str()); - SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, - static_cast(const_cast(host_.c_str()))); - return true; - }); - - if (ssl) { - socket.ssl = ssl; - return true; - } - - shutdown_socket(socket); - close_socket(socket); - return false; -} - -inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) { - shutdown_ssl_impl(socket, shutdown_gracefully); -} - -inline void SSLClient::shutdown_ssl_impl(Socket &socket, - bool shutdown_gracefully) { - if (socket.sock == INVALID_SOCKET) { - assert(socket.ssl == nullptr); - return; - } - if (socket.ssl) { - detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully); - socket.ssl = nullptr; - } - assert(socket.ssl == nullptr); -} - -inline bool -SSLClient::process_socket(const Socket &socket, - std::function callback) { - assert(socket.ssl); - return detail::process_client_socket_ssl( - socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_, - write_timeout_sec_, write_timeout_usec_, std::move(callback)); + return true; + }, + [&](SSL *ssl) { + SSL_set_tlsext_host_name(ssl, host_.c_str()); + return true; + }, + [&](SSL * /*ssl*/, Stream &strm, bool /*last_connection*/, + bool &connection_close) { + return process_request(strm, req, res, connection_close); + }); } inline bool SSLClient::is_ssl() const { return true; } @@ -8837,11 +2681,10 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { auto type = GEN_DNS; - struct in6_addr addr6 {}; - struct in_addr addr {}; + struct in6_addr addr6; + struct in_addr addr; size_t addr_len = 0; -#ifndef __MINGW32__ if (inet_pton(AF_INET6, host_.c_str(), &addr6)) { type = GEN_IPADD; addr_len = sizeof(struct in6_addr); @@ -8849,42 +2692,42 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { type = GEN_IPADD; addr_len = sizeof(struct in_addr); } -#endif auto alt_names = static_cast( X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr)); if (alt_names) { auto dsn_matched = false; - auto ip_matched = false; + auto ip_mached = false; auto count = sk_GENERAL_NAME_num(alt_names); - for (decltype(count) i = 0; i < count && !dsn_matched; i++) { + for (auto i = 0; i < count && !dsn_matched; i++) { auto val = sk_GENERAL_NAME_value(alt_names, i); if (val->type == type) { - auto name = - reinterpret_cast(ASN1_STRING_get0_data(val->d.ia5)); - auto name_len = static_cast(ASN1_STRING_length(val->d.ia5)); + auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5); + auto name_len = (size_t)ASN1_STRING_length(val->d.ia5); - switch (type) { - case GEN_DNS: dsn_matched = check_host_name(name, name_len); break; + if (strlen(name) == name_len) { + switch (type) { + case GEN_DNS: dsn_matched = check_host_name(name, name_len); break; - case GEN_IPADD: - if (!memcmp(&addr6, name, addr_len) || - !memcmp(&addr, name, addr_len)) { - ip_matched = true; + case GEN_IPADD: + if (!memcmp(&addr6, name, addr_len) || + !memcmp(&addr, name, addr_len)) { + ip_mached = true; + } + break; } - break; } } } - if (dsn_matched || ip_matched) { ret = true; } + if (dsn_matched || ip_mached) { ret = true; } } - GENERAL_NAMES_free(const_cast( - reinterpret_cast(alt_names))); + GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names); + return ret; } @@ -8896,9 +2739,7 @@ inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const { auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, name, sizeof(name)); - if (name_len != -1) { - return check_host_name(name, static_cast(name_len)); - } + if (name_len != -1) { return check_host_name(name, name_len); } } return false; @@ -8913,7 +2754,7 @@ inline bool SSLClient::check_host_name(const char *pattern, std::vector pattern_components; detail::split(&pattern[0], &pattern[pattern_len], '.', [&](const char *b, const char *e) { - pattern_components.emplace_back(b, e); + pattern_components.emplace_back(std::string(b, e)); }); if (host_components_.size() != pattern_components.size()) { return false; } @@ -8933,509 +2774,6 @@ inline bool SSLClient::check_host_name(const char *pattern, } #endif -// Universal client implementation -inline Client::Client(const std::string &scheme_host_port) - : Client(scheme_host_port, std::string(), std::string()) {} - -inline Client::Client(const std::string &scheme_host_port, - const std::string &client_cert_path, - const std::string &client_key_path) { - const static std::regex re( - R"((?:([a-z]+):\/\/)?(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)"); - - std::smatch m; - if (std::regex_match(scheme_host_port, m, re)) { - auto scheme = m[1].str(); - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - if (!scheme.empty() && (scheme != "http" && scheme != "https")) { -#else - if (!scheme.empty() && scheme != "http") { -#endif -#ifndef CPPHTTPLIB_NO_EXCEPTIONS - std::string msg = "'" + scheme + "' scheme is not supported."; - throw std::invalid_argument(msg); -#endif - return; - } - - auto is_ssl = scheme == "https"; - - auto host = m[2].str(); - if (host.empty()) { host = m[3].str(); } - - auto port_str = m[4].str(); - auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80); - - if (is_ssl) { -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - cli_ = detail::make_unique(host, port, client_cert_path, - client_key_path); - is_ssl_ = is_ssl; -#endif - } else { - cli_ = detail::make_unique(host, port, client_cert_path, - client_key_path); - } - } else { - cli_ = detail::make_unique(scheme_host_port, 80, - client_cert_path, client_key_path); - } -} - -inline Client::Client(const std::string &host, int port) - : cli_(detail::make_unique(host, port)) {} - -inline Client::Client(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path) - : cli_(detail::make_unique(host, port, client_cert_path, - client_key_path)) {} - -inline Client::~Client() = default; - -inline bool Client::is_valid() const { - return cli_ != nullptr && cli_->is_valid(); -} - -inline Result Client::Get(const std::string &path) { return cli_->Get(path); } -inline Result Client::Get(const std::string &path, const Headers &headers) { - return cli_->Get(path, headers); -} -inline Result Client::Get(const std::string &path, Progress progress) { - return cli_->Get(path, std::move(progress)); -} -inline Result Client::Get(const std::string &path, const Headers &headers, - Progress progress) { - return cli_->Get(path, headers, std::move(progress)); -} -inline Result Client::Get(const std::string &path, - ContentReceiver content_receiver) { - return cli_->Get(path, std::move(content_receiver)); -} -inline Result Client::Get(const std::string &path, const Headers &headers, - ContentReceiver content_receiver) { - return cli_->Get(path, headers, std::move(content_receiver)); -} -inline Result Client::Get(const std::string &path, - ContentReceiver content_receiver, Progress progress) { - return cli_->Get(path, std::move(content_receiver), std::move(progress)); -} -inline Result Client::Get(const std::string &path, const Headers &headers, - ContentReceiver content_receiver, Progress progress) { - return cli_->Get(path, headers, std::move(content_receiver), - std::move(progress)); -} -inline Result Client::Get(const std::string &path, - ResponseHandler response_handler, - ContentReceiver content_receiver) { - return cli_->Get(path, std::move(response_handler), - std::move(content_receiver)); -} -inline Result Client::Get(const std::string &path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver) { - return cli_->Get(path, headers, std::move(response_handler), - std::move(content_receiver)); -} -inline Result Client::Get(const std::string &path, - ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress) { - return cli_->Get(path, std::move(response_handler), - std::move(content_receiver), std::move(progress)); -} -inline Result Client::Get(const std::string &path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress) { - return cli_->Get(path, headers, std::move(response_handler), - std::move(content_receiver), std::move(progress)); -} -inline Result Client::Get(const std::string &path, const Params ¶ms, - const Headers &headers, Progress progress) { - return cli_->Get(path, params, headers, std::move(progress)); -} -inline Result Client::Get(const std::string &path, const Params ¶ms, - const Headers &headers, - ContentReceiver content_receiver, Progress progress) { - return cli_->Get(path, params, headers, std::move(content_receiver), - std::move(progress)); -} -inline Result Client::Get(const std::string &path, const Params ¶ms, - const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress) { - return cli_->Get(path, params, headers, std::move(response_handler), - std::move(content_receiver), std::move(progress)); -} - -inline Result Client::Head(const std::string &path) { return cli_->Head(path); } -inline Result Client::Head(const std::string &path, const Headers &headers) { - return cli_->Head(path, headers); -} - -inline Result Client::Post(const std::string &path) { return cli_->Post(path); } -inline Result Client::Post(const std::string &path, const Headers &headers) { - return cli_->Post(path, headers); -} -inline Result Client::Post(const std::string &path, const char *body, - size_t content_length, - const std::string &content_type) { - return cli_->Post(path, body, content_length, content_type); -} -inline Result Client::Post(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type) { - return cli_->Post(path, headers, body, content_length, content_type); -} -inline Result Client::Post(const std::string &path, const std::string &body, - const std::string &content_type) { - return cli_->Post(path, body, content_type); -} -inline Result Client::Post(const std::string &path, const Headers &headers, - const std::string &body, - const std::string &content_type) { - return cli_->Post(path, headers, body, content_type); -} -inline Result Client::Post(const std::string &path, size_t content_length, - ContentProvider content_provider, - const std::string &content_type) { - return cli_->Post(path, content_length, std::move(content_provider), - content_type); -} -inline Result Client::Post(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type) { - return cli_->Post(path, std::move(content_provider), content_type); -} -inline Result Client::Post(const std::string &path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const std::string &content_type) { - return cli_->Post(path, headers, content_length, std::move(content_provider), - content_type); -} -inline Result Client::Post(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type) { - return cli_->Post(path, headers, std::move(content_provider), content_type); -} -inline Result Client::Post(const std::string &path, const Params ¶ms) { - return cli_->Post(path, params); -} -inline Result Client::Post(const std::string &path, const Headers &headers, - const Params ¶ms) { - return cli_->Post(path, headers, params); -} -inline Result Client::Post(const std::string &path, - const MultipartFormDataItems &items) { - return cli_->Post(path, items); -} -inline Result Client::Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items) { - return cli_->Post(path, headers, items); -} -inline Result Client::Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const std::string &boundary) { - return cli_->Post(path, headers, items, boundary); -} -inline Result -Client::Post(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items) { - return cli_->Post(path, headers, items, provider_items); -} -inline Result Client::Put(const std::string &path) { return cli_->Put(path); } -inline Result Client::Put(const std::string &path, const char *body, - size_t content_length, - const std::string &content_type) { - return cli_->Put(path, body, content_length, content_type); -} -inline Result Client::Put(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type) { - return cli_->Put(path, headers, body, content_length, content_type); -} -inline Result Client::Put(const std::string &path, const std::string &body, - const std::string &content_type) { - return cli_->Put(path, body, content_type); -} -inline Result Client::Put(const std::string &path, const Headers &headers, - const std::string &body, - const std::string &content_type) { - return cli_->Put(path, headers, body, content_type); -} -inline Result Client::Put(const std::string &path, size_t content_length, - ContentProvider content_provider, - const std::string &content_type) { - return cli_->Put(path, content_length, std::move(content_provider), - content_type); -} -inline Result Client::Put(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type) { - return cli_->Put(path, std::move(content_provider), content_type); -} -inline Result Client::Put(const std::string &path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const std::string &content_type) { - return cli_->Put(path, headers, content_length, std::move(content_provider), - content_type); -} -inline Result Client::Put(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type) { - return cli_->Put(path, headers, std::move(content_provider), content_type); -} -inline Result Client::Put(const std::string &path, const Params ¶ms) { - return cli_->Put(path, params); -} -inline Result Client::Put(const std::string &path, const Headers &headers, - const Params ¶ms) { - return cli_->Put(path, headers, params); -} -inline Result Client::Put(const std::string &path, - const MultipartFormDataItems &items) { - return cli_->Put(path, items); -} -inline Result Client::Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items) { - return cli_->Put(path, headers, items); -} -inline Result Client::Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const std::string &boundary) { - return cli_->Put(path, headers, items, boundary); -} -inline Result -Client::Put(const std::string &path, const Headers &headers, - const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items) { - return cli_->Put(path, headers, items, provider_items); -} -inline Result Client::Patch(const std::string &path) { - return cli_->Patch(path); -} -inline Result Client::Patch(const std::string &path, const char *body, - size_t content_length, - const std::string &content_type) { - return cli_->Patch(path, body, content_length, content_type); -} -inline Result Client::Patch(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type) { - return cli_->Patch(path, headers, body, content_length, content_type); -} -inline Result Client::Patch(const std::string &path, const std::string &body, - const std::string &content_type) { - return cli_->Patch(path, body, content_type); -} -inline Result Client::Patch(const std::string &path, const Headers &headers, - const std::string &body, - const std::string &content_type) { - return cli_->Patch(path, headers, body, content_type); -} -inline Result Client::Patch(const std::string &path, size_t content_length, - ContentProvider content_provider, - const std::string &content_type) { - return cli_->Patch(path, content_length, std::move(content_provider), - content_type); -} -inline Result Client::Patch(const std::string &path, - ContentProviderWithoutLength content_provider, - const std::string &content_type) { - return cli_->Patch(path, std::move(content_provider), content_type); -} -inline Result Client::Patch(const std::string &path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const std::string &content_type) { - return cli_->Patch(path, headers, content_length, std::move(content_provider), - content_type); -} -inline Result Client::Patch(const std::string &path, const Headers &headers, - ContentProviderWithoutLength content_provider, - const std::string &content_type) { - return cli_->Patch(path, headers, std::move(content_provider), content_type); -} -inline Result Client::Delete(const std::string &path) { - return cli_->Delete(path); -} -inline Result Client::Delete(const std::string &path, const Headers &headers) { - return cli_->Delete(path, headers); -} -inline Result Client::Delete(const std::string &path, const char *body, - size_t content_length, - const std::string &content_type) { - return cli_->Delete(path, body, content_length, content_type); -} -inline Result Client::Delete(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type) { - return cli_->Delete(path, headers, body, content_length, content_type); -} -inline Result Client::Delete(const std::string &path, const std::string &body, - const std::string &content_type) { - return cli_->Delete(path, body, content_type); -} -inline Result Client::Delete(const std::string &path, const Headers &headers, - const std::string &body, - const std::string &content_type) { - return cli_->Delete(path, headers, body, content_type); -} -inline Result Client::Options(const std::string &path) { - return cli_->Options(path); -} -inline Result Client::Options(const std::string &path, const Headers &headers) { - return cli_->Options(path, headers); -} - -inline bool Client::send(Request &req, Response &res, Error &error) { - return cli_->send(req, res, error); -} - -inline Result Client::send(const Request &req) { return cli_->send(req); } - -inline void Client::stop() { cli_->stop(); } - -inline std::string Client::host() const { return cli_->host(); } - -inline int Client::port() const { return cli_->port(); } - -inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); } - -inline socket_t Client::socket() const { return cli_->socket(); } - -inline void -Client::set_hostname_addr_map(std::map addr_map) { - cli_->set_hostname_addr_map(std::move(addr_map)); -} - -inline void Client::set_default_headers(Headers headers) { - cli_->set_default_headers(std::move(headers)); -} - -inline void Client::set_header_writer( - std::function const &writer) { - cli_->set_header_writer(writer); -} - -inline void Client::set_address_family(int family) { - cli_->set_address_family(family); -} - -inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); } - -inline void Client::set_socket_options(SocketOptions socket_options) { - cli_->set_socket_options(std::move(socket_options)); -} - -inline void Client::set_connection_timeout(time_t sec, time_t usec) { - cli_->set_connection_timeout(sec, usec); -} - -inline void Client::set_read_timeout(time_t sec, time_t usec) { - cli_->set_read_timeout(sec, usec); -} - -inline void Client::set_write_timeout(time_t sec, time_t usec) { - cli_->set_write_timeout(sec, usec); -} - -inline void Client::set_basic_auth(const std::string &username, - const std::string &password) { - cli_->set_basic_auth(username, password); -} -inline void Client::set_bearer_token_auth(const std::string &token) { - cli_->set_bearer_token_auth(token); -} -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -inline void Client::set_digest_auth(const std::string &username, - const std::string &password) { - cli_->set_digest_auth(username, password); -} -#endif - -inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); } -inline void Client::set_follow_location(bool on) { - cli_->set_follow_location(on); -} - -inline void Client::set_url_encode(bool on) { cli_->set_url_encode(on); } - -inline void Client::set_compress(bool on) { cli_->set_compress(on); } - -inline void Client::set_decompress(bool on) { cli_->set_decompress(on); } - -inline void Client::set_interface(const std::string &intf) { - cli_->set_interface(intf); -} - -inline void Client::set_proxy(const std::string &host, int port) { - cli_->set_proxy(host, port); -} -inline void Client::set_proxy_basic_auth(const std::string &username, - const std::string &password) { - cli_->set_proxy_basic_auth(username, password); -} -inline void Client::set_proxy_bearer_token_auth(const std::string &token) { - cli_->set_proxy_bearer_token_auth(token); -} -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -inline void Client::set_proxy_digest_auth(const std::string &username, - const std::string &password) { - cli_->set_proxy_digest_auth(username, password); -} -#endif - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -inline void Client::enable_server_certificate_verification(bool enabled) { - cli_->enable_server_certificate_verification(enabled); -} -#endif - -inline void Client::set_logger(Logger logger) { - cli_->set_logger(std::move(logger)); -} - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path, - const std::string &ca_cert_dir_path) { - cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path); -} - -inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) { - if (is_ssl_) { - static_cast(*cli_).set_ca_cert_store(ca_cert_store); - } else { - cli_->set_ca_cert_store(ca_cert_store); - } -} - -inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) { - set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size)); -} - -inline long Client::get_openssl_verify_result() const { - if (is_ssl_) { - return static_cast(*cli_).get_openssl_verify_result(); - } - return -1; // NOTE: -1 doesn't match any of X509_V_ERR_??? -} - -inline SSL_CTX *Client::ssl_context() const { - if (is_ssl_) { return static_cast(*cli_).ssl_context(); } - return nullptr; -} -#endif - -// ---------------------------------------------------------------------------- - } // namespace httplib -#if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL) -#undef poll -#endif - #endif // CPPHTTPLIB_HTTPLIB_H diff --git a/ext/curl-7.58.0/Win32/include/curl/curl.h b/ext/curl-7.58.0/Win32/include/curl/curl.h new file mode 100644 index 000000000..7680acd18 --- /dev/null +++ b/ext/curl-7.58.0/Win32/include/curl/curl.h @@ -0,0 +1,2751 @@ +#ifndef __CURL_CURL_H +#define __CURL_CURL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* + * If you have libcurl problems, all docs and details are found here: + * https://curl.haxx.se/libcurl/ + * + * curl-library mailing list subscription and unsubscription web interface: + * https://cool.haxx.se/mailman/listinfo/curl-library/ + */ + +#ifdef CURL_NO_OLDIES +#define CURL_STRICTER +#endif + +#include "curlver.h" /* libcurl version defines */ +#include "system.h" /* determine things run-time */ + +/* + * Define WIN32 when build target is Win32 API + */ + +#if (defined(_WIN32) || defined(__WIN32__)) && \ + !defined(WIN32) && !defined(__SYMBIAN32__) +#define WIN32 +#endif + +#include +#include + +#if defined(__FreeBSD__) && (__FreeBSD__ >= 2) +/* Needed for __FreeBSD_version symbol definition */ +#include +#endif + +/* The include stuff here below is mainly for time_t! */ +#include +#include + +#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__) +#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || \ + defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H)) +/* The check above prevents the winsock2 inclusion if winsock.h already was + included, since they can't co-exist without problems */ +#include +#include +#endif +#endif + +/* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish + libc5-based Linux systems. Only include it on systems that are known to + require it! */ +#if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \ + defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \ + defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \ + defined(__CYGWIN__) || \ + (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) +#include +#endif + +#if !defined(WIN32) && !defined(_WIN32_WCE) +#include +#endif + +#if !defined(WIN32) && !defined(__WATCOMC__) && !defined(__VXWORKS__) +#include +#endif + +#ifdef __BEOS__ +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER) +typedef struct Curl_easy CURL; +typedef struct Curl_share CURLSH; +#else +typedef void CURL; +typedef void CURLSH; +#endif + +/* + * libcurl external API function linkage decorations. + */ + +#ifdef CURL_STATICLIB +# define CURL_EXTERN +#elif defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__) +# if defined(BUILDING_LIBCURL) +# define CURL_EXTERN __declspec(dllexport) +# else +# define CURL_EXTERN __declspec(dllimport) +# endif +#elif defined(BUILDING_LIBCURL) && defined(CURL_HIDDEN_SYMBOLS) +# define CURL_EXTERN CURL_EXTERN_SYMBOL +#else +# define CURL_EXTERN +#endif + +#ifndef curl_socket_typedef +/* socket typedef */ +#if defined(WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H) +typedef SOCKET curl_socket_t; +#define CURL_SOCKET_BAD INVALID_SOCKET +#else +typedef int curl_socket_t; +#define CURL_SOCKET_BAD -1 +#endif +#define curl_socket_typedef +#endif /* curl_socket_typedef */ + +/* enum for the different supported SSL backends */ +typedef enum { + CURLSSLBACKEND_NONE = 0, + CURLSSLBACKEND_OPENSSL = 1, + CURLSSLBACKEND_GNUTLS = 2, + CURLSSLBACKEND_NSS = 3, + CURLSSLBACKEND_OBSOLETE4 = 4, /* Was QSOSSL. */ + CURLSSLBACKEND_GSKIT = 5, + CURLSSLBACKEND_POLARSSL = 6, + CURLSSLBACKEND_WOLFSSL = 7, + CURLSSLBACKEND_SCHANNEL = 8, + CURLSSLBACKEND_DARWINSSL = 9, + CURLSSLBACKEND_AXTLS = 10, + CURLSSLBACKEND_MBEDTLS = 11 +} curl_sslbackend; + +/* aliases for library clones and renames */ +#define CURLSSLBACKEND_LIBRESSL CURLSSLBACKEND_OPENSSL +#define CURLSSLBACKEND_BORINGSSL CURLSSLBACKEND_OPENSSL +#define CURLSSLBACKEND_CYASSL CURLSSLBACKEND_WOLFSSL + +struct curl_httppost { + struct curl_httppost *next; /* next entry in the list */ + char *name; /* pointer to allocated name */ + long namelength; /* length of name length */ + char *contents; /* pointer to allocated data contents */ + long contentslength; /* length of contents field, see also + CURL_HTTPPOST_LARGE */ + char *buffer; /* pointer to allocated buffer contents */ + long bufferlength; /* length of buffer field */ + char *contenttype; /* Content-Type */ + struct curl_slist *contentheader; /* list of extra headers for this form */ + struct curl_httppost *more; /* if one field name has more than one + file, this link should link to following + files */ + long flags; /* as defined below */ + +/* specified content is a file name */ +#define CURL_HTTPPOST_FILENAME (1<<0) +/* specified content is a file name */ +#define CURL_HTTPPOST_READFILE (1<<1) +/* name is only stored pointer do not free in formfree */ +#define CURL_HTTPPOST_PTRNAME (1<<2) +/* contents is only stored pointer do not free in formfree */ +#define CURL_HTTPPOST_PTRCONTENTS (1<<3) +/* upload file from buffer */ +#define CURL_HTTPPOST_BUFFER (1<<4) +/* upload file from pointer contents */ +#define CURL_HTTPPOST_PTRBUFFER (1<<5) +/* upload file contents by using the regular read callback to get the data and + pass the given pointer as custom pointer */ +#define CURL_HTTPPOST_CALLBACK (1<<6) +/* use size in 'contentlen', added in 7.46.0 */ +#define CURL_HTTPPOST_LARGE (1<<7) + + char *showfilename; /* The file name to show. If not set, the + actual file name will be used (if this + is a file part) */ + void *userp; /* custom pointer used for + HTTPPOST_CALLBACK posts */ + curl_off_t contentlen; /* alternative length of contents + field. Used if CURL_HTTPPOST_LARGE is + set. Added in 7.46.0 */ +}; + +/* This is the CURLOPT_PROGRESSFUNCTION callback proto. It is now considered + deprecated but was the only choice up until 7.31.0 */ +typedef int (*curl_progress_callback)(void *clientp, + double dltotal, + double dlnow, + double ultotal, + double ulnow); + +/* This is the CURLOPT_XFERINFOFUNCTION callback proto. It was introduced in + 7.32.0, it avoids floating point and provides more detailed information. */ +typedef int (*curl_xferinfo_callback)(void *clientp, + curl_off_t dltotal, + curl_off_t dlnow, + curl_off_t ultotal, + curl_off_t ulnow); + +#ifndef CURL_MAX_READ_SIZE + /* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */ +#define CURL_MAX_READ_SIZE 524288 +#endif + +#ifndef CURL_MAX_WRITE_SIZE + /* Tests have proven that 20K is a very bad buffer size for uploads on + Windows, while 16K for some odd reason performed a lot better. + We do the ifndef check to allow this value to easier be changed at build + time for those who feel adventurous. The practical minimum is about + 400 bytes since libcurl uses a buffer of this size as a scratch area + (unrelated to network send operations). */ +#define CURL_MAX_WRITE_SIZE 16384 +#endif + +#ifndef CURL_MAX_HTTP_HEADER +/* The only reason to have a max limit for this is to avoid the risk of a bad + server feeding libcurl with a never-ending header that will cause reallocs + infinitely */ +#define CURL_MAX_HTTP_HEADER (100*1024) +#endif + +/* This is a magic return code for the write callback that, when returned, + will signal libcurl to pause receiving on the current transfer. */ +#define CURL_WRITEFUNC_PAUSE 0x10000001 + +typedef size_t (*curl_write_callback)(char *buffer, + size_t size, + size_t nitems, + void *outstream); + + + +/* enumeration of file types */ +typedef enum { + CURLFILETYPE_FILE = 0, + CURLFILETYPE_DIRECTORY, + CURLFILETYPE_SYMLINK, + CURLFILETYPE_DEVICE_BLOCK, + CURLFILETYPE_DEVICE_CHAR, + CURLFILETYPE_NAMEDPIPE, + CURLFILETYPE_SOCKET, + CURLFILETYPE_DOOR, /* is possible only on Sun Solaris now */ + + CURLFILETYPE_UNKNOWN /* should never occur */ +} curlfiletype; + +#define CURLFINFOFLAG_KNOWN_FILENAME (1<<0) +#define CURLFINFOFLAG_KNOWN_FILETYPE (1<<1) +#define CURLFINFOFLAG_KNOWN_TIME (1<<2) +#define CURLFINFOFLAG_KNOWN_PERM (1<<3) +#define CURLFINFOFLAG_KNOWN_UID (1<<4) +#define CURLFINFOFLAG_KNOWN_GID (1<<5) +#define CURLFINFOFLAG_KNOWN_SIZE (1<<6) +#define CURLFINFOFLAG_KNOWN_HLINKCOUNT (1<<7) + +/* Content of this structure depends on information which is known and is + achievable (e.g. by FTP LIST parsing). Please see the url_easy_setopt(3) man + page for callbacks returning this structure -- some fields are mandatory, + some others are optional. The FLAG field has special meaning. */ +struct curl_fileinfo { + char *filename; + curlfiletype filetype; + time_t time; + unsigned int perm; + int uid; + int gid; + curl_off_t size; + long int hardlinks; + + struct { + /* If some of these fields is not NULL, it is a pointer to b_data. */ + char *time; + char *perm; + char *user; + char *group; + char *target; /* pointer to the target filename of a symlink */ + } strings; + + unsigned int flags; + + /* used internally */ + char *b_data; + size_t b_size; + size_t b_used; +}; + +/* return codes for CURLOPT_CHUNK_BGN_FUNCTION */ +#define CURL_CHUNK_BGN_FUNC_OK 0 +#define CURL_CHUNK_BGN_FUNC_FAIL 1 /* tell the lib to end the task */ +#define CURL_CHUNK_BGN_FUNC_SKIP 2 /* skip this chunk over */ + +/* if splitting of data transfer is enabled, this callback is called before + download of an individual chunk started. Note that parameter "remains" works + only for FTP wildcard downloading (for now), otherwise is not used */ +typedef long (*curl_chunk_bgn_callback)(const void *transfer_info, + void *ptr, + int remains); + +/* return codes for CURLOPT_CHUNK_END_FUNCTION */ +#define CURL_CHUNK_END_FUNC_OK 0 +#define CURL_CHUNK_END_FUNC_FAIL 1 /* tell the lib to end the task */ + +/* If splitting of data transfer is enabled this callback is called after + download of an individual chunk finished. + Note! After this callback was set then it have to be called FOR ALL chunks. + Even if downloading of this chunk was skipped in CHUNK_BGN_FUNC. + This is the reason why we don't need "transfer_info" parameter in this + callback and we are not interested in "remains" parameter too. */ +typedef long (*curl_chunk_end_callback)(void *ptr); + +/* return codes for FNMATCHFUNCTION */ +#define CURL_FNMATCHFUNC_MATCH 0 /* string corresponds to the pattern */ +#define CURL_FNMATCHFUNC_NOMATCH 1 /* pattern doesn't match the string */ +#define CURL_FNMATCHFUNC_FAIL 2 /* an error occurred */ + +/* callback type for wildcard downloading pattern matching. If the + string matches the pattern, return CURL_FNMATCHFUNC_MATCH value, etc. */ +typedef int (*curl_fnmatch_callback)(void *ptr, + const char *pattern, + const char *string); + +/* These are the return codes for the seek callbacks */ +#define CURL_SEEKFUNC_OK 0 +#define CURL_SEEKFUNC_FAIL 1 /* fail the entire transfer */ +#define CURL_SEEKFUNC_CANTSEEK 2 /* tell libcurl seeking can't be done, so + libcurl might try other means instead */ +typedef int (*curl_seek_callback)(void *instream, + curl_off_t offset, + int origin); /* 'whence' */ + +/* This is a return code for the read callback that, when returned, will + signal libcurl to immediately abort the current transfer. */ +#define CURL_READFUNC_ABORT 0x10000000 +/* This is a return code for the read callback that, when returned, will + signal libcurl to pause sending data on the current transfer. */ +#define CURL_READFUNC_PAUSE 0x10000001 + +typedef size_t (*curl_read_callback)(char *buffer, + size_t size, + size_t nitems, + void *instream); + +typedef enum { + CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */ + CURLSOCKTYPE_ACCEPT, /* socket created by accept() call */ + CURLSOCKTYPE_LAST /* never use */ +} curlsocktype; + +/* The return code from the sockopt_callback can signal information back + to libcurl: */ +#define CURL_SOCKOPT_OK 0 +#define CURL_SOCKOPT_ERROR 1 /* causes libcurl to abort and return + CURLE_ABORTED_BY_CALLBACK */ +#define CURL_SOCKOPT_ALREADY_CONNECTED 2 + +typedef int (*curl_sockopt_callback)(void *clientp, + curl_socket_t curlfd, + curlsocktype purpose); + +struct curl_sockaddr { + int family; + int socktype; + int protocol; + unsigned int addrlen; /* addrlen was a socklen_t type before 7.18.0 but it + turned really ugly and painful on the systems that + lack this type */ + struct sockaddr addr; +}; + +typedef curl_socket_t +(*curl_opensocket_callback)(void *clientp, + curlsocktype purpose, + struct curl_sockaddr *address); + +typedef int +(*curl_closesocket_callback)(void *clientp, curl_socket_t item); + +typedef enum { + CURLIOE_OK, /* I/O operation successful */ + CURLIOE_UNKNOWNCMD, /* command was unknown to callback */ + CURLIOE_FAILRESTART, /* failed to restart the read */ + CURLIOE_LAST /* never use */ +} curlioerr; + +typedef enum { + CURLIOCMD_NOP, /* no operation */ + CURLIOCMD_RESTARTREAD, /* restart the read stream from start */ + CURLIOCMD_LAST /* never use */ +} curliocmd; + +typedef curlioerr (*curl_ioctl_callback)(CURL *handle, + int cmd, + void *clientp); + +#ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS +/* + * The following typedef's are signatures of malloc, free, realloc, strdup and + * calloc respectively. Function pointers of these types can be passed to the + * curl_global_init_mem() function to set user defined memory management + * callback routines. + */ +typedef void *(*curl_malloc_callback)(size_t size); +typedef void (*curl_free_callback)(void *ptr); +typedef void *(*curl_realloc_callback)(void *ptr, size_t size); +typedef char *(*curl_strdup_callback)(const char *str); +typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size); + +#define CURL_DID_MEMORY_FUNC_TYPEDEFS +#endif + +/* the kind of data that is passed to information_callback*/ +typedef enum { + CURLINFO_TEXT = 0, + CURLINFO_HEADER_IN, /* 1 */ + CURLINFO_HEADER_OUT, /* 2 */ + CURLINFO_DATA_IN, /* 3 */ + CURLINFO_DATA_OUT, /* 4 */ + CURLINFO_SSL_DATA_IN, /* 5 */ + CURLINFO_SSL_DATA_OUT, /* 6 */ + CURLINFO_END +} curl_infotype; + +typedef int (*curl_debug_callback) + (CURL *handle, /* the handle/transfer this concerns */ + curl_infotype type, /* what kind of data */ + char *data, /* points to the data */ + size_t size, /* size of the data pointed to */ + void *userptr); /* whatever the user please */ + +/* All possible error codes from all sorts of curl functions. Future versions + may return other values, stay prepared. + + Always add new return codes last. Never *EVER* remove any. The return + codes must remain the same! + */ + +typedef enum { + CURLE_OK = 0, + CURLE_UNSUPPORTED_PROTOCOL, /* 1 */ + CURLE_FAILED_INIT, /* 2 */ + CURLE_URL_MALFORMAT, /* 3 */ + CURLE_NOT_BUILT_IN, /* 4 - [was obsoleted in August 2007 for + 7.17.0, reused in April 2011 for 7.21.5] */ + CURLE_COULDNT_RESOLVE_PROXY, /* 5 */ + CURLE_COULDNT_RESOLVE_HOST, /* 6 */ + CURLE_COULDNT_CONNECT, /* 7 */ + CURLE_WEIRD_SERVER_REPLY, /* 8 */ + CURLE_REMOTE_ACCESS_DENIED, /* 9 a service was denied by the server + due to lack of access - when login fails + this is not returned. */ + CURLE_FTP_ACCEPT_FAILED, /* 10 - [was obsoleted in April 2006 for + 7.15.4, reused in Dec 2011 for 7.24.0]*/ + CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */ + CURLE_FTP_ACCEPT_TIMEOUT, /* 12 - timeout occurred accepting server + [was obsoleted in August 2007 for 7.17.0, + reused in Dec 2011 for 7.24.0]*/ + CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */ + CURLE_FTP_WEIRD_227_FORMAT, /* 14 */ + CURLE_FTP_CANT_GET_HOST, /* 15 */ + CURLE_HTTP2, /* 16 - A problem in the http2 framing layer. + [was obsoleted in August 2007 for 7.17.0, + reused in July 2014 for 7.38.0] */ + CURLE_FTP_COULDNT_SET_TYPE, /* 17 */ + CURLE_PARTIAL_FILE, /* 18 */ + CURLE_FTP_COULDNT_RETR_FILE, /* 19 */ + CURLE_OBSOLETE20, /* 20 - NOT USED */ + CURLE_QUOTE_ERROR, /* 21 - quote command failure */ + CURLE_HTTP_RETURNED_ERROR, /* 22 */ + CURLE_WRITE_ERROR, /* 23 */ + CURLE_OBSOLETE24, /* 24 - NOT USED */ + CURLE_UPLOAD_FAILED, /* 25 - failed upload "command" */ + CURLE_READ_ERROR, /* 26 - couldn't open/read from file */ + CURLE_OUT_OF_MEMORY, /* 27 */ + /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error + instead of a memory allocation error if CURL_DOES_CONVERSIONS + is defined + */ + CURLE_OPERATION_TIMEDOUT, /* 28 - the timeout time was reached */ + CURLE_OBSOLETE29, /* 29 - NOT USED */ + CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */ + CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */ + CURLE_OBSOLETE32, /* 32 - NOT USED */ + CURLE_RANGE_ERROR, /* 33 - RANGE "command" didn't work */ + CURLE_HTTP_POST_ERROR, /* 34 */ + CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */ + CURLE_BAD_DOWNLOAD_RESUME, /* 36 - couldn't resume download */ + CURLE_FILE_COULDNT_READ_FILE, /* 37 */ + CURLE_LDAP_CANNOT_BIND, /* 38 */ + CURLE_LDAP_SEARCH_FAILED, /* 39 */ + CURLE_OBSOLETE40, /* 40 - NOT USED */ + CURLE_FUNCTION_NOT_FOUND, /* 41 - NOT USED starting with 7.53.0 */ + CURLE_ABORTED_BY_CALLBACK, /* 42 */ + CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */ + CURLE_OBSOLETE44, /* 44 - NOT USED */ + CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */ + CURLE_OBSOLETE46, /* 46 - NOT USED */ + CURLE_TOO_MANY_REDIRECTS, /* 47 - catch endless re-direct loops */ + CURLE_UNKNOWN_OPTION, /* 48 - User specified an unknown option */ + CURLE_TELNET_OPTION_SYNTAX, /* 49 - Malformed telnet option */ + CURLE_OBSOLETE50, /* 50 - NOT USED */ + CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint + wasn't verified fine */ + CURLE_GOT_NOTHING, /* 52 - when this is a specific error */ + CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */ + CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as + default */ + CURLE_SEND_ERROR, /* 55 - failed sending network data */ + CURLE_RECV_ERROR, /* 56 - failure in receiving network data */ + CURLE_OBSOLETE57, /* 57 - NOT IN USE */ + CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */ + CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */ + CURLE_SSL_CACERT, /* 60 - problem with the CA cert (path?) */ + CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized/bad encoding */ + CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */ + CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ + CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ + CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind + that failed */ + CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ + CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not + accepted and we failed to login */ + CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ + CURLE_TFTP_PERM, /* 69 - permission problem on server */ + CURLE_REMOTE_DISK_FULL, /* 70 - out of disk space on server */ + CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */ + CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */ + CURLE_REMOTE_FILE_EXISTS, /* 73 - File already exists */ + CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */ + CURLE_CONV_FAILED, /* 75 - conversion failed */ + CURLE_CONV_REQD, /* 76 - caller must register conversion + callbacks using curl_easy_setopt options + CURLOPT_CONV_FROM_NETWORK_FUNCTION, + CURLOPT_CONV_TO_NETWORK_FUNCTION, and + CURLOPT_CONV_FROM_UTF8_FUNCTION */ + CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing + or wrong format */ + CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ + CURLE_SSH, /* 79 - error from the SSH layer, somewhat + generic so the error message will be of + interest when this has happened */ + + CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL + connection */ + CURLE_AGAIN, /* 81 - socket is not ready for send/recv, + wait till it's ready and try again (Added + in 7.18.2) */ + CURLE_SSL_CRL_BADFILE, /* 82 - could not load CRL file, missing or + wrong format (Added in 7.19.0) */ + CURLE_SSL_ISSUER_ERROR, /* 83 - Issuer check failed. (Added in + 7.19.0) */ + CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */ + CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers */ + CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ids */ + CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */ + CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ + CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the + session will be queued */ + CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not + match */ + CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */ + CURLE_HTTP2_STREAM, /* 92 - stream error in HTTP/2 framing layer + */ + CURL_LAST /* never use! */ +} CURLcode; + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Previously obsolete error code re-used in 7.38.0 */ +#define CURLE_OBSOLETE16 CURLE_HTTP2 + +/* Previously obsolete error codes re-used in 7.24.0 */ +#define CURLE_OBSOLETE10 CURLE_FTP_ACCEPT_FAILED +#define CURLE_OBSOLETE12 CURLE_FTP_ACCEPT_TIMEOUT + +/* compatibility with older names */ +#define CURLOPT_ENCODING CURLOPT_ACCEPT_ENCODING +#define CURLE_FTP_WEIRD_SERVER_REPLY CURLE_WEIRD_SERVER_REPLY + +/* The following were added in 7.21.5, April 2011 */ +#define CURLE_UNKNOWN_TELNET_OPTION CURLE_UNKNOWN_OPTION + +/* The following were added in 7.17.1 */ +/* These are scheduled to disappear by 2009 */ +#define CURLE_SSL_PEER_CERTIFICATE CURLE_PEER_FAILED_VERIFICATION + +/* The following were added in 7.17.0 */ +/* These are scheduled to disappear by 2009 */ +#define CURLE_OBSOLETE CURLE_OBSOLETE50 /* no one should be using this! */ +#define CURLE_BAD_PASSWORD_ENTERED CURLE_OBSOLETE46 +#define CURLE_BAD_CALLING_ORDER CURLE_OBSOLETE44 +#define CURLE_FTP_USER_PASSWORD_INCORRECT CURLE_OBSOLETE10 +#define CURLE_FTP_CANT_RECONNECT CURLE_OBSOLETE16 +#define CURLE_FTP_COULDNT_GET_SIZE CURLE_OBSOLETE32 +#define CURLE_FTP_COULDNT_SET_ASCII CURLE_OBSOLETE29 +#define CURLE_FTP_WEIRD_USER_REPLY CURLE_OBSOLETE12 +#define CURLE_FTP_WRITE_ERROR CURLE_OBSOLETE20 +#define CURLE_LIBRARY_NOT_FOUND CURLE_OBSOLETE40 +#define CURLE_MALFORMAT_USER CURLE_OBSOLETE24 +#define CURLE_SHARE_IN_USE CURLE_OBSOLETE57 +#define CURLE_URL_MALFORMAT_USER CURLE_NOT_BUILT_IN + +#define CURLE_FTP_ACCESS_DENIED CURLE_REMOTE_ACCESS_DENIED +#define CURLE_FTP_COULDNT_SET_BINARY CURLE_FTP_COULDNT_SET_TYPE +#define CURLE_FTP_QUOTE_ERROR CURLE_QUOTE_ERROR +#define CURLE_TFTP_DISKFULL CURLE_REMOTE_DISK_FULL +#define CURLE_TFTP_EXISTS CURLE_REMOTE_FILE_EXISTS +#define CURLE_HTTP_RANGE_ERROR CURLE_RANGE_ERROR +#define CURLE_FTP_SSL_FAILED CURLE_USE_SSL_FAILED + +/* The following were added earlier */ + +#define CURLE_OPERATION_TIMEOUTED CURLE_OPERATION_TIMEDOUT + +#define CURLE_HTTP_NOT_FOUND CURLE_HTTP_RETURNED_ERROR +#define CURLE_HTTP_PORT_FAILED CURLE_INTERFACE_FAILED +#define CURLE_FTP_COULDNT_STOR_FILE CURLE_UPLOAD_FAILED + +#define CURLE_FTP_PARTIAL_FILE CURLE_PARTIAL_FILE +#define CURLE_FTP_BAD_DOWNLOAD_RESUME CURLE_BAD_DOWNLOAD_RESUME + +/* This was the error code 50 in 7.7.3 and a few earlier versions, this + is no longer used by libcurl but is instead #defined here only to not + make programs break */ +#define CURLE_ALREADY_COMPLETE 99999 + +/* Provide defines for really old option names */ +#define CURLOPT_FILE CURLOPT_WRITEDATA /* name changed in 7.9.7 */ +#define CURLOPT_INFILE CURLOPT_READDATA /* name changed in 7.9.7 */ +#define CURLOPT_WRITEHEADER CURLOPT_HEADERDATA + +/* Since long deprecated options with no code in the lib that does anything + with them. */ +#define CURLOPT_WRITEINFO CURLOPT_OBSOLETE40 +#define CURLOPT_CLOSEPOLICY CURLOPT_OBSOLETE72 + +#endif /*!CURL_NO_OLDIES*/ + +/* This prototype applies to all conversion callbacks */ +typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length); + +typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ + void *ssl_ctx, /* actually an + OpenSSL SSL_CTX */ + void *userptr); + +typedef enum { + CURLPROXY_HTTP = 0, /* added in 7.10, new in 7.19.4 default is to use + CONNECT HTTP/1.1 */ + CURLPROXY_HTTP_1_0 = 1, /* added in 7.19.4, force to use CONNECT + HTTP/1.0 */ + CURLPROXY_HTTPS = 2, /* added in 7.52.0 */ + CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already + in 7.10 */ + CURLPROXY_SOCKS5 = 5, /* added in 7.10 */ + CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */ + CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the + host name rather than the IP address. added + in 7.18.0 */ +} curl_proxytype; /* this enum was added in 7.10 */ + +/* + * Bitmasks for CURLOPT_HTTPAUTH and CURLOPT_PROXYAUTH options: + * + * CURLAUTH_NONE - No HTTP authentication + * CURLAUTH_BASIC - HTTP Basic authentication (default) + * CURLAUTH_DIGEST - HTTP Digest authentication + * CURLAUTH_NEGOTIATE - HTTP Negotiate (SPNEGO) authentication + * CURLAUTH_GSSNEGOTIATE - Alias for CURLAUTH_NEGOTIATE (deprecated) + * CURLAUTH_NTLM - HTTP NTLM authentication + * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavour + * CURLAUTH_NTLM_WB - HTTP NTLM authentication delegated to winbind helper + * CURLAUTH_ONLY - Use together with a single other type to force no + * authentication or just that single type + * CURLAUTH_ANY - All fine types set + * CURLAUTH_ANYSAFE - All fine types except Basic + */ + +#define CURLAUTH_NONE ((unsigned long)0) +#define CURLAUTH_BASIC (((unsigned long)1)<<0) +#define CURLAUTH_DIGEST (((unsigned long)1)<<1) +#define CURLAUTH_NEGOTIATE (((unsigned long)1)<<2) +/* Deprecated since the advent of CURLAUTH_NEGOTIATE */ +#define CURLAUTH_GSSNEGOTIATE CURLAUTH_NEGOTIATE +/* Used for CURLOPT_SOCKS5_AUTH to stay terminologically correct */ +#define CURLAUTH_GSSAPI CURLAUTH_NEGOTIATE +#define CURLAUTH_NTLM (((unsigned long)1)<<3) +#define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) +#define CURLAUTH_NTLM_WB (((unsigned long)1)<<5) +#define CURLAUTH_ONLY (((unsigned long)1)<<31) +#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) +#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE)) + +#define CURLSSH_AUTH_ANY ~0 /* all types supported by the server */ +#define CURLSSH_AUTH_NONE 0 /* none allowed, silly but complete */ +#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */ +#define CURLSSH_AUTH_PASSWORD (1<<1) /* password */ +#define CURLSSH_AUTH_HOST (1<<2) /* host key files */ +#define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */ +#define CURLSSH_AUTH_AGENT (1<<4) /* agent (ssh-agent, pageant...) */ +#define CURLSSH_AUTH_GSSAPI (1<<5) /* gssapi (kerberos, ...) */ +#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY + +#define CURLGSSAPI_DELEGATION_NONE 0 /* no delegation (default) */ +#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1<<0) /* if permitted by policy */ +#define CURLGSSAPI_DELEGATION_FLAG (1<<1) /* delegate always */ + +#define CURL_ERROR_SIZE 256 + +enum curl_khtype { + CURLKHTYPE_UNKNOWN, + CURLKHTYPE_RSA1, + CURLKHTYPE_RSA, + CURLKHTYPE_DSS, + CURLKHTYPE_ECDSA, + CURLKHTYPE_ED25519 +}; + +struct curl_khkey { + const char *key; /* points to a zero-terminated string encoded with base64 + if len is zero, otherwise to the "raw" data */ + size_t len; + enum curl_khtype keytype; +}; + +/* this is the set of return values expected from the curl_sshkeycallback + callback */ +enum curl_khstat { + CURLKHSTAT_FINE_ADD_TO_FILE, + CURLKHSTAT_FINE, + CURLKHSTAT_REJECT, /* reject the connection, return an error */ + CURLKHSTAT_DEFER, /* do not accept it, but we can't answer right now so + this causes a CURLE_DEFER error but otherwise the + connection will be left intact etc */ + CURLKHSTAT_LAST /* not for use, only a marker for last-in-list */ +}; + +/* this is the set of status codes pass in to the callback */ +enum curl_khmatch { + CURLKHMATCH_OK, /* match */ + CURLKHMATCH_MISMATCH, /* host found, key mismatch! */ + CURLKHMATCH_MISSING, /* no matching host/key found */ + CURLKHMATCH_LAST /* not for use, only a marker for last-in-list */ +}; + +typedef int + (*curl_sshkeycallback) (CURL *easy, /* easy handle */ + const struct curl_khkey *knownkey, /* known */ + const struct curl_khkey *foundkey, /* found */ + enum curl_khmatch, /* libcurl's view on the keys */ + void *clientp); /* custom pointer passed from app */ + +/* parameter for the CURLOPT_USE_SSL option */ +typedef enum { + CURLUSESSL_NONE, /* do not attempt to use SSL */ + CURLUSESSL_TRY, /* try using SSL, proceed anyway otherwise */ + CURLUSESSL_CONTROL, /* SSL for the control connection or fail */ + CURLUSESSL_ALL, /* SSL for all communication or fail */ + CURLUSESSL_LAST /* not an option, never use */ +} curl_usessl; + +/* Definition of bits for the CURLOPT_SSL_OPTIONS argument: */ + +/* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the + name of improving interoperability with older servers. Some SSL libraries + have introduced work-arounds for this flaw but those work-arounds sometimes + make the SSL communication fail. To regain functionality with those broken + servers, a user can this way allow the vulnerability back. */ +#define CURLSSLOPT_ALLOW_BEAST (1<<0) + +/* - NO_REVOKE tells libcurl to disable certificate revocation checks for those + SSL backends where such behavior is present. */ +#define CURLSSLOPT_NO_REVOKE (1<<1) + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Backwards compatibility with older names */ +/* These are scheduled to disappear by 2009 */ + +#define CURLFTPSSL_NONE CURLUSESSL_NONE +#define CURLFTPSSL_TRY CURLUSESSL_TRY +#define CURLFTPSSL_CONTROL CURLUSESSL_CONTROL +#define CURLFTPSSL_ALL CURLUSESSL_ALL +#define CURLFTPSSL_LAST CURLUSESSL_LAST +#define curl_ftpssl curl_usessl +#endif /*!CURL_NO_OLDIES*/ + +/* parameter for the CURLOPT_FTP_SSL_CCC option */ +typedef enum { + CURLFTPSSL_CCC_NONE, /* do not send CCC */ + CURLFTPSSL_CCC_PASSIVE, /* Let the server initiate the shutdown */ + CURLFTPSSL_CCC_ACTIVE, /* Initiate the shutdown */ + CURLFTPSSL_CCC_LAST /* not an option, never use */ +} curl_ftpccc; + +/* parameter for the CURLOPT_FTPSSLAUTH option */ +typedef enum { + CURLFTPAUTH_DEFAULT, /* let libcurl decide */ + CURLFTPAUTH_SSL, /* use "AUTH SSL" */ + CURLFTPAUTH_TLS, /* use "AUTH TLS" */ + CURLFTPAUTH_LAST /* not an option, never use */ +} curl_ftpauth; + +/* parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */ +typedef enum { + CURLFTP_CREATE_DIR_NONE, /* do NOT create missing dirs! */ + CURLFTP_CREATE_DIR, /* (FTP/SFTP) if CWD fails, try MKD and then CWD + again if MKD succeeded, for SFTP this does + similar magic */ + CURLFTP_CREATE_DIR_RETRY, /* (FTP only) if CWD fails, try MKD and then CWD + again even if MKD failed! */ + CURLFTP_CREATE_DIR_LAST /* not an option, never use */ +} curl_ftpcreatedir; + +/* parameter for the CURLOPT_FTP_FILEMETHOD option */ +typedef enum { + CURLFTPMETHOD_DEFAULT, /* let libcurl pick */ + CURLFTPMETHOD_MULTICWD, /* single CWD operation for each path part */ + CURLFTPMETHOD_NOCWD, /* no CWD at all */ + CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */ + CURLFTPMETHOD_LAST /* not an option, never use */ +} curl_ftpmethod; + +/* bitmask defines for CURLOPT_HEADEROPT */ +#define CURLHEADER_UNIFIED 0 +#define CURLHEADER_SEPARATE (1<<0) + +/* CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */ +#define CURLPROTO_HTTP (1<<0) +#define CURLPROTO_HTTPS (1<<1) +#define CURLPROTO_FTP (1<<2) +#define CURLPROTO_FTPS (1<<3) +#define CURLPROTO_SCP (1<<4) +#define CURLPROTO_SFTP (1<<5) +#define CURLPROTO_TELNET (1<<6) +#define CURLPROTO_LDAP (1<<7) +#define CURLPROTO_LDAPS (1<<8) +#define CURLPROTO_DICT (1<<9) +#define CURLPROTO_FILE (1<<10) +#define CURLPROTO_TFTP (1<<11) +#define CURLPROTO_IMAP (1<<12) +#define CURLPROTO_IMAPS (1<<13) +#define CURLPROTO_POP3 (1<<14) +#define CURLPROTO_POP3S (1<<15) +#define CURLPROTO_SMTP (1<<16) +#define CURLPROTO_SMTPS (1<<17) +#define CURLPROTO_RTSP (1<<18) +#define CURLPROTO_RTMP (1<<19) +#define CURLPROTO_RTMPT (1<<20) +#define CURLPROTO_RTMPE (1<<21) +#define CURLPROTO_RTMPTE (1<<22) +#define CURLPROTO_RTMPS (1<<23) +#define CURLPROTO_RTMPTS (1<<24) +#define CURLPROTO_GOPHER (1<<25) +#define CURLPROTO_SMB (1<<26) +#define CURLPROTO_SMBS (1<<27) +#define CURLPROTO_ALL (~0) /* enable everything */ + +/* long may be 32 or 64 bits, but we should never depend on anything else + but 32 */ +#define CURLOPTTYPE_LONG 0 +#define CURLOPTTYPE_OBJECTPOINT 10000 +#define CURLOPTTYPE_STRINGPOINT 10000 +#define CURLOPTTYPE_FUNCTIONPOINT 20000 +#define CURLOPTTYPE_OFF_T 30000 + +/* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the + string options from the header file */ + +/* name is uppercase CURLOPT_, + type is one of the defined CURLOPTTYPE_ + number is unique identifier */ +#ifdef CINIT +#undef CINIT +#endif + +#ifdef CURL_ISOCPP +#define CINIT(na,t,nu) CURLOPT_ ## na = CURLOPTTYPE_ ## t + nu +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define LONG CURLOPTTYPE_LONG +#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define STRINGPOINT CURLOPTTYPE_OBJECTPOINT +#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT +#define OFF_T CURLOPTTYPE_OFF_T +#define CINIT(name,type,number) CURLOPT_/**/name = type + number +#endif + +/* + * This macro-mania below setups the CURLOPT_[what] enum, to be used with + * curl_easy_setopt(). The first argument in the CINIT() macro is the [what] + * word. + */ + +typedef enum { + /* This is the FILE * or void * the regular output should be written to. */ + CINIT(WRITEDATA, OBJECTPOINT, 1), + + /* The full URL to get/put */ + CINIT(URL, STRINGPOINT, 2), + + /* Port number to connect to, if other than default. */ + CINIT(PORT, LONG, 3), + + /* Name of proxy to use. */ + CINIT(PROXY, STRINGPOINT, 4), + + /* "user:password;options" to use when fetching. */ + CINIT(USERPWD, STRINGPOINT, 5), + + /* "user:password" to use with proxy. */ + CINIT(PROXYUSERPWD, STRINGPOINT, 6), + + /* Range to get, specified as an ASCII string. */ + CINIT(RANGE, STRINGPOINT, 7), + + /* not used */ + + /* Specified file stream to upload from (use as input): */ + CINIT(READDATA, OBJECTPOINT, 9), + + /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE + * bytes big. */ + CINIT(ERRORBUFFER, OBJECTPOINT, 10), + + /* Function that will be called to store the output (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11), + + /* Function that will be called to read the input (instead of fread). The + * parameters will use fread() syntax, make sure to follow them. */ + CINIT(READFUNCTION, FUNCTIONPOINT, 12), + + /* Time-out the read operation after this amount of seconds */ + CINIT(TIMEOUT, LONG, 13), + + /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about + * how large the file being sent really is. That allows better error + * checking and better verifies that the upload was successful. -1 means + * unknown size. + * + * For large file support, there is also a _LARGE version of the key + * which takes an off_t type, allowing platforms with larger off_t + * sizes to handle larger files. See below for INFILESIZE_LARGE. + */ + CINIT(INFILESIZE, LONG, 14), + + /* POST static input fields. */ + CINIT(POSTFIELDS, OBJECTPOINT, 15), + + /* Set the referrer page (needed by some CGIs) */ + CINIT(REFERER, STRINGPOINT, 16), + + /* Set the FTP PORT string (interface name, named or numerical IP address) + Use i.e '-' to use default address. */ + CINIT(FTPPORT, STRINGPOINT, 17), + + /* Set the User-Agent string (examined by some CGIs) */ + CINIT(USERAGENT, STRINGPOINT, 18), + + /* If the download receives less than "low speed limit" bytes/second + * during "low speed time" seconds, the operations is aborted. + * You could i.e if you have a pretty high speed connection, abort if + * it is less than 2000 bytes/sec during 20 seconds. + */ + + /* Set the "low speed limit" */ + CINIT(LOW_SPEED_LIMIT, LONG, 19), + + /* Set the "low speed time" */ + CINIT(LOW_SPEED_TIME, LONG, 20), + + /* Set the continuation offset. + * + * Note there is also a _LARGE version of this key which uses + * off_t types, allowing for large file offsets on platforms which + * use larger-than-32-bit off_t's. Look below for RESUME_FROM_LARGE. + */ + CINIT(RESUME_FROM, LONG, 21), + + /* Set cookie in request: */ + CINIT(COOKIE, STRINGPOINT, 22), + + /* This points to a linked list of headers, struct curl_slist kind. This + list is also used for RTSP (in spite of its name) */ + CINIT(HTTPHEADER, OBJECTPOINT, 23), + + /* This points to a linked list of post entries, struct curl_httppost */ + CINIT(HTTPPOST, OBJECTPOINT, 24), + + /* name of the file keeping your private SSL-certificate */ + CINIT(SSLCERT, STRINGPOINT, 25), + + /* password for the SSL or SSH private key */ + CINIT(KEYPASSWD, STRINGPOINT, 26), + + /* send TYPE parameter? */ + CINIT(CRLF, LONG, 27), + + /* send linked-list of QUOTE commands */ + CINIT(QUOTE, OBJECTPOINT, 28), + + /* send FILE * or void * to store headers to, if you use a callback it + is simply passed to the callback unmodified */ + CINIT(HEADERDATA, OBJECTPOINT, 29), + + /* point to a file to read the initial cookies from, also enables + "cookie awareness" */ + CINIT(COOKIEFILE, STRINGPOINT, 31), + + /* What version to specifically try to use. + See CURL_SSLVERSION defines below. */ + CINIT(SSLVERSION, LONG, 32), + + /* What kind of HTTP time condition to use, see defines */ + CINIT(TIMECONDITION, LONG, 33), + + /* Time to use with the above condition. Specified in number of seconds + since 1 Jan 1970 */ + CINIT(TIMEVALUE, LONG, 34), + + /* 35 = OBSOLETE */ + + /* Custom request, for customizing the get command like + HTTP: DELETE, TRACE and others + FTP: to use a different list command + */ + CINIT(CUSTOMREQUEST, STRINGPOINT, 36), + + /* FILE handle to use instead of stderr */ + CINIT(STDERR, OBJECTPOINT, 37), + + /* 38 is not used */ + + /* send linked-list of post-transfer QUOTE commands */ + CINIT(POSTQUOTE, OBJECTPOINT, 39), + + CINIT(OBSOLETE40, OBJECTPOINT, 40), /* OBSOLETE, do not use! */ + + CINIT(VERBOSE, LONG, 41), /* talk a lot */ + CINIT(HEADER, LONG, 42), /* throw the header out too */ + CINIT(NOPROGRESS, LONG, 43), /* shut off the progress meter */ + CINIT(NOBODY, LONG, 44), /* use HEAD to get http document */ + CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 400 */ + CINIT(UPLOAD, LONG, 46), /* this is an upload */ + CINIT(POST, LONG, 47), /* HTTP POST method */ + CINIT(DIRLISTONLY, LONG, 48), /* bare names when listing directories */ + + CINIT(APPEND, LONG, 50), /* Append instead of overwrite on upload! */ + + /* Specify whether to read the user+password from the .netrc or the URL. + * This must be one of the CURL_NETRC_* enums below. */ + CINIT(NETRC, LONG, 51), + + CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */ + + CINIT(TRANSFERTEXT, LONG, 53), /* transfer data in text/ASCII format */ + CINIT(PUT, LONG, 54), /* HTTP PUT */ + + /* 55 = OBSOLETE */ + + /* DEPRECATED + * Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_progress_callback + * prototype defines. */ + CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56), + + /* Data passed to the CURLOPT_PROGRESSFUNCTION and CURLOPT_XFERINFOFUNCTION + callbacks */ + CINIT(PROGRESSDATA, OBJECTPOINT, 57), +#define CURLOPT_XFERINFODATA CURLOPT_PROGRESSDATA + + /* We want the referrer field set automatically when following locations */ + CINIT(AUTOREFERER, LONG, 58), + + /* Port of the proxy, can be set in the proxy string as well with: + "[host]:[port]" */ + CINIT(PROXYPORT, LONG, 59), + + /* size of the POST input data, if strlen() is not good to use */ + CINIT(POSTFIELDSIZE, LONG, 60), + + /* tunnel non-http operations through a HTTP proxy */ + CINIT(HTTPPROXYTUNNEL, LONG, 61), + + /* Set the interface string to use as outgoing network interface */ + CINIT(INTERFACE, STRINGPOINT, 62), + + /* Set the krb4/5 security level, this also enables krb4/5 awareness. This + * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string + * is set but doesn't match one of these, 'private' will be used. */ + CINIT(KRBLEVEL, STRINGPOINT, 63), + + /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ + CINIT(SSL_VERIFYPEER, LONG, 64), + + /* The CApath or CAfile used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAINFO, STRINGPOINT, 65), + + /* 66 = OBSOLETE */ + /* 67 = OBSOLETE */ + + /* Maximum number of http redirects to follow */ + CINIT(MAXREDIRS, LONG, 68), + + /* Pass a long set to 1 to get the date of the requested document (if + possible)! Pass a zero to shut it off. */ + CINIT(FILETIME, LONG, 69), + + /* This points to a linked list of telnet options */ + CINIT(TELNETOPTIONS, OBJECTPOINT, 70), + + /* Max amount of cached alive connections */ + CINIT(MAXCONNECTS, LONG, 71), + + CINIT(OBSOLETE72, LONG, 72), /* OBSOLETE, do not use! */ + + /* 73 = OBSOLETE */ + + /* Set to explicitly use a new connection for the upcoming transfer. + Do not use this unless you're absolutely sure of this, as it makes the + operation slower and is less friendly for the network. */ + CINIT(FRESH_CONNECT, LONG, 74), + + /* Set to explicitly forbid the upcoming transfer's connection to be re-used + when done. Do not use this unless you're absolutely sure of this, as it + makes the operation slower and is less friendly for the network. */ + CINIT(FORBID_REUSE, LONG, 75), + + /* Set to a file name that contains random data for libcurl to use to + seed the random engine when doing SSL connects. */ + CINIT(RANDOM_FILE, STRINGPOINT, 76), + + /* Set to the Entropy Gathering Daemon socket pathname */ + CINIT(EGDSOCKET, STRINGPOINT, 77), + + /* Time-out connect operations after this amount of seconds, if connects are + OK within this time, then fine... This only aborts the connect phase. */ + CINIT(CONNECTTIMEOUT, LONG, 78), + + /* Function that will be called to store headers (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79), + + /* Set this to force the HTTP request to get back to GET. Only really usable + if POST, PUT or a custom request have been used first. + */ + CINIT(HTTPGET, LONG, 80), + + /* Set if we should verify the Common name from the peer certificate in ssl + * handshake, set 1 to check existence, 2 to ensure that it matches the + * provided hostname. */ + CINIT(SSL_VERIFYHOST, LONG, 81), + + /* Specify which file name to write all known cookies in after completed + operation. Set file name to "-" (dash) to make it go to stdout. */ + CINIT(COOKIEJAR, STRINGPOINT, 82), + + /* Specify which SSL ciphers to use */ + CINIT(SSL_CIPHER_LIST, STRINGPOINT, 83), + + /* Specify which HTTP version to use! This must be set to one of the + CURL_HTTP_VERSION* enums set below. */ + CINIT(HTTP_VERSION, LONG, 84), + + /* Specifically switch on or off the FTP engine's use of the EPSV command. By + default, that one will always be attempted before the more traditional + PASV command. */ + CINIT(FTP_USE_EPSV, LONG, 85), + + /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */ + CINIT(SSLCERTTYPE, STRINGPOINT, 86), + + /* name of the file keeping your private SSL-key */ + CINIT(SSLKEY, STRINGPOINT, 87), + + /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */ + CINIT(SSLKEYTYPE, STRINGPOINT, 88), + + /* crypto engine for the SSL-sub system */ + CINIT(SSLENGINE, STRINGPOINT, 89), + + /* set the crypto engine for the SSL-sub system as default + the param has no meaning... + */ + CINIT(SSLENGINE_DEFAULT, LONG, 90), + + /* Non-zero value means to use the global dns cache */ + CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* DEPRECATED, do not use! */ + + /* DNS cache timeout */ + CINIT(DNS_CACHE_TIMEOUT, LONG, 92), + + /* send linked-list of pre-transfer QUOTE commands */ + CINIT(PREQUOTE, OBJECTPOINT, 93), + + /* set the debug function */ + CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94), + + /* set the data for the debug function */ + CINIT(DEBUGDATA, OBJECTPOINT, 95), + + /* mark this as start of a cookie session */ + CINIT(COOKIESESSION, LONG, 96), + + /* The CApath directory used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAPATH, STRINGPOINT, 97), + + /* Instruct libcurl to use a smaller receive buffer */ + CINIT(BUFFERSIZE, LONG, 98), + + /* Instruct libcurl to not use any signal/alarm handlers, even when using + timeouts. This option is useful for multi-threaded applications. + See libcurl-the-guide for more background information. */ + CINIT(NOSIGNAL, LONG, 99), + + /* Provide a CURLShare for mutexing non-ts data */ + CINIT(SHARE, OBJECTPOINT, 100), + + /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), + CURLPROXY_HTTPS, CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and + CURLPROXY_SOCKS5. */ + CINIT(PROXYTYPE, LONG, 101), + + /* Set the Accept-Encoding string. Use this to tell a server you would like + the response to be compressed. Before 7.21.6, this was known as + CURLOPT_ENCODING */ + CINIT(ACCEPT_ENCODING, STRINGPOINT, 102), + + /* Set pointer to private data */ + CINIT(PRIVATE, OBJECTPOINT, 103), + + /* Set aliases for HTTP 200 in the HTTP Response header */ + CINIT(HTTP200ALIASES, OBJECTPOINT, 104), + + /* Continue to send authentication (user+password) when following locations, + even when hostname changed. This can potentially send off the name + and password to whatever host the server decides. */ + CINIT(UNRESTRICTED_AUTH, LONG, 105), + + /* Specifically switch on or off the FTP engine's use of the EPRT command ( + it also disables the LPRT attempt). By default, those ones will always be + attempted before the good old traditional PORT command. */ + CINIT(FTP_USE_EPRT, LONG, 106), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_USERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(HTTPAUTH, LONG, 107), + + /* Set the ssl context callback function, currently only for OpenSSL ssl_ctx + in second argument. The function must be matching the + curl_ssl_ctx_callback proto. */ + CINIT(SSL_CTX_FUNCTION, FUNCTIONPOINT, 108), + + /* Set the userdata for the ssl context callback function's third + argument */ + CINIT(SSL_CTX_DATA, OBJECTPOINT, 109), + + /* FTP Option that causes missing dirs to be created on the remote server. + In 7.19.4 we introduced the convenience enums for this option using the + CURLFTP_CREATE_DIR prefix. + */ + CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_PROXYUSERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(PROXYAUTH, LONG, 111), + + /* FTP option that changes the timeout, in seconds, associated with + getting a response. This is different from transfer timeout time and + essentially places a demand on the FTP server to acknowledge commands + in a timely manner. */ + CINIT(FTP_RESPONSE_TIMEOUT, LONG, 112), +#define CURLOPT_SERVER_RESPONSE_TIMEOUT CURLOPT_FTP_RESPONSE_TIMEOUT + + /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to + tell libcurl to resolve names to those IP versions only. This only has + affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */ + CINIT(IPRESOLVE, LONG, 113), + + /* Set this option to limit the size of a file that will be downloaded from + an HTTP or FTP server. + + Note there is also _LARGE version which adds large file support for + platforms which have larger off_t sizes. See MAXFILESIZE_LARGE below. */ + CINIT(MAXFILESIZE, LONG, 114), + + /* See the comment for INFILESIZE above, but in short, specifies + * the size of the file being uploaded. -1 means unknown. + */ + CINIT(INFILESIZE_LARGE, OFF_T, 115), + + /* Sets the continuation offset. There is also a LONG version of this; + * look above for RESUME_FROM. + */ + CINIT(RESUME_FROM_LARGE, OFF_T, 116), + + /* Sets the maximum size of data that will be downloaded from + * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. + */ + CINIT(MAXFILESIZE_LARGE, OFF_T, 117), + + /* Set this option to the file name of your .netrc file you want libcurl + to parse (using the CURLOPT_NETRC option). If not set, libcurl will do + a poor attempt to find the user's home directory and check for a .netrc + file in there. */ + CINIT(NETRC_FILE, STRINGPOINT, 118), + + /* Enable SSL/TLS for FTP, pick one of: + CURLUSESSL_TRY - try using SSL, proceed anyway otherwise + CURLUSESSL_CONTROL - SSL for the control connection or fail + CURLUSESSL_ALL - SSL for all communication or fail + */ + CINIT(USE_SSL, LONG, 119), + + /* The _LARGE version of the standard POSTFIELDSIZE option */ + CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120), + + /* Enable/disable the TCP Nagle algorithm */ + CINIT(TCP_NODELAY, LONG, 121), + + /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 123 OBSOLETE. Gone in 7.16.0 */ + /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 127 OBSOLETE. Gone in 7.16.0 */ + /* 128 OBSOLETE. Gone in 7.16.0 */ + + /* When FTP over SSL/TLS is selected (with CURLOPT_USE_SSL), this option + can be used to change libcurl's default action which is to first try + "AUTH SSL" and then "AUTH TLS" in this order, and proceed when a OK + response has been received. + + Available parameters are: + CURLFTPAUTH_DEFAULT - let libcurl decide + CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS + CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL + */ + CINIT(FTPSSLAUTH, LONG, 129), + + CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130), + CINIT(IOCTLDATA, OBJECTPOINT, 131), + + /* 132 OBSOLETE. Gone in 7.16.0 */ + /* 133 OBSOLETE. Gone in 7.16.0 */ + + /* zero terminated string for pass on to the FTP server when asked for + "account" info */ + CINIT(FTP_ACCOUNT, STRINGPOINT, 134), + + /* feed cookie into cookie engine */ + CINIT(COOKIELIST, STRINGPOINT, 135), + + /* ignore Content-Length */ + CINIT(IGNORE_CONTENT_LENGTH, LONG, 136), + + /* Set to non-zero to skip the IP address received in a 227 PASV FTP server + response. Typically used for FTP-SSL purposes but is not restricted to + that. libcurl will then instead use the same IP address it used for the + control connection. */ + CINIT(FTP_SKIP_PASV_IP, LONG, 137), + + /* Select "file method" to use when doing FTP, see the curl_ftpmethod + above. */ + CINIT(FTP_FILEMETHOD, LONG, 138), + + /* Local port number to bind the socket to */ + CINIT(LOCALPORT, LONG, 139), + + /* Number of ports to try, including the first one set with LOCALPORT. + Thus, setting it to 1 will make no additional attempts but the first. + */ + CINIT(LOCALPORTRANGE, LONG, 140), + + /* no transfer, set up connection and let application use the socket by + extracting it with CURLINFO_LASTSOCKET */ + CINIT(CONNECT_ONLY, LONG, 141), + + /* Function that will be called to convert from the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_FROM_NETWORK_FUNCTION, FUNCTIONPOINT, 142), + + /* Function that will be called to convert to the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_TO_NETWORK_FUNCTION, FUNCTIONPOINT, 143), + + /* Function that will be called to convert from UTF8 + (instead of using the iconv calls in libcurl) + Note that this is used only for SSL certificate processing */ + CINIT(CONV_FROM_UTF8_FUNCTION, FUNCTIONPOINT, 144), + + /* if the connection proceeds too quickly then need to slow it down */ + /* limit-rate: maximum number of bytes per second to send or receive */ + CINIT(MAX_SEND_SPEED_LARGE, OFF_T, 145), + CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146), + + /* Pointer to command string to send if USER/PASS fails. */ + CINIT(FTP_ALTERNATIVE_TO_USER, STRINGPOINT, 147), + + /* callback function for setting socket options */ + CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148), + CINIT(SOCKOPTDATA, OBJECTPOINT, 149), + + /* set to 0 to disable session ID re-use for this transfer, default is + enabled (== 1) */ + CINIT(SSL_SESSIONID_CACHE, LONG, 150), + + /* allowed SSH authentication methods */ + CINIT(SSH_AUTH_TYPES, LONG, 151), + + /* Used by scp/sftp to do public/private key authentication */ + CINIT(SSH_PUBLIC_KEYFILE, STRINGPOINT, 152), + CINIT(SSH_PRIVATE_KEYFILE, STRINGPOINT, 153), + + /* Send CCC (Clear Command Channel) after authentication */ + CINIT(FTP_SSL_CCC, LONG, 154), + + /* Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */ + CINIT(TIMEOUT_MS, LONG, 155), + CINIT(CONNECTTIMEOUT_MS, LONG, 156), + + /* set to zero to disable the libcurl's decoding and thus pass the raw body + data to the application even when it is encoded/compressed */ + CINIT(HTTP_TRANSFER_DECODING, LONG, 157), + CINIT(HTTP_CONTENT_DECODING, LONG, 158), + + /* Permission used when creating new files and directories on the remote + server for protocols that support it, SFTP/SCP/FILE */ + CINIT(NEW_FILE_PERMS, LONG, 159), + CINIT(NEW_DIRECTORY_PERMS, LONG, 160), + + /* Set the behaviour of POST when redirecting. Values must be set to one + of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */ + CINIT(POSTREDIR, LONG, 161), + + /* used by scp/sftp to verify the host's public key */ + CINIT(SSH_HOST_PUBLIC_KEY_MD5, STRINGPOINT, 162), + + /* Callback function for opening socket (instead of socket(2)). Optionally, + callback is able change the address or refuse to connect returning + CURL_SOCKET_BAD. The callback should have type + curl_opensocket_callback */ + CINIT(OPENSOCKETFUNCTION, FUNCTIONPOINT, 163), + CINIT(OPENSOCKETDATA, OBJECTPOINT, 164), + + /* POST volatile input fields. */ + CINIT(COPYPOSTFIELDS, OBJECTPOINT, 165), + + /* set transfer mode (;type=) when doing FTP via an HTTP proxy */ + CINIT(PROXY_TRANSFER_MODE, LONG, 166), + + /* Callback function for seeking in the input stream */ + CINIT(SEEKFUNCTION, FUNCTIONPOINT, 167), + CINIT(SEEKDATA, OBJECTPOINT, 168), + + /* CRL file */ + CINIT(CRLFILE, STRINGPOINT, 169), + + /* Issuer certificate */ + CINIT(ISSUERCERT, STRINGPOINT, 170), + + /* (IPv6) Address scope */ + CINIT(ADDRESS_SCOPE, LONG, 171), + + /* Collect certificate chain info and allow it to get retrievable with + CURLINFO_CERTINFO after the transfer is complete. */ + CINIT(CERTINFO, LONG, 172), + + /* "name" and "pwd" to use when fetching. */ + CINIT(USERNAME, STRINGPOINT, 173), + CINIT(PASSWORD, STRINGPOINT, 174), + + /* "name" and "pwd" to use with Proxy when fetching. */ + CINIT(PROXYUSERNAME, STRINGPOINT, 175), + CINIT(PROXYPASSWORD, STRINGPOINT, 176), + + /* Comma separated list of hostnames defining no-proxy zones. These should + match both hostnames directly, and hostnames within a domain. For + example, local.com will match local.com and www.local.com, but NOT + notlocal.com or www.notlocal.com. For compatibility with other + implementations of this, .local.com will be considered to be the same as + local.com. A single * is the only valid wildcard, and effectively + disables the use of proxy. */ + CINIT(NOPROXY, STRINGPOINT, 177), + + /* block size for TFTP transfers */ + CINIT(TFTP_BLKSIZE, LONG, 178), + + /* Socks Service */ + CINIT(SOCKS5_GSSAPI_SERVICE, STRINGPOINT, 179), /* DEPRECATED, do not use! */ + + /* Socks Service */ + CINIT(SOCKS5_GSSAPI_NEC, LONG, 180), + + /* set the bitmask for the protocols that are allowed to be used for the + transfer, which thus helps the app which takes URLs from users or other + external inputs and want to restrict what protocol(s) to deal + with. Defaults to CURLPROTO_ALL. */ + CINIT(PROTOCOLS, LONG, 181), + + /* set the bitmask for the protocols that libcurl is allowed to follow to, + as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs + to be set in both bitmasks to be allowed to get redirected to. Defaults + to all protocols except FILE and SCP. */ + CINIT(REDIR_PROTOCOLS, LONG, 182), + + /* set the SSH knownhost file name to use */ + CINIT(SSH_KNOWNHOSTS, STRINGPOINT, 183), + + /* set the SSH host key callback, must point to a curl_sshkeycallback + function */ + CINIT(SSH_KEYFUNCTION, FUNCTIONPOINT, 184), + + /* set the SSH host key callback custom pointer */ + CINIT(SSH_KEYDATA, OBJECTPOINT, 185), + + /* set the SMTP mail originator */ + CINIT(MAIL_FROM, STRINGPOINT, 186), + + /* set the list of SMTP mail receiver(s) */ + CINIT(MAIL_RCPT, OBJECTPOINT, 187), + + /* FTP: send PRET before PASV */ + CINIT(FTP_USE_PRET, LONG, 188), + + /* RTSP request method (OPTIONS, SETUP, PLAY, etc...) */ + CINIT(RTSP_REQUEST, LONG, 189), + + /* The RTSP session identifier */ + CINIT(RTSP_SESSION_ID, STRINGPOINT, 190), + + /* The RTSP stream URI */ + CINIT(RTSP_STREAM_URI, STRINGPOINT, 191), + + /* The Transport: header to use in RTSP requests */ + CINIT(RTSP_TRANSPORT, STRINGPOINT, 192), + + /* Manually initialize the client RTSP CSeq for this handle */ + CINIT(RTSP_CLIENT_CSEQ, LONG, 193), + + /* Manually initialize the server RTSP CSeq for this handle */ + CINIT(RTSP_SERVER_CSEQ, LONG, 194), + + /* The stream to pass to INTERLEAVEFUNCTION. */ + CINIT(INTERLEAVEDATA, OBJECTPOINT, 195), + + /* Let the application define a custom write method for RTP data */ + CINIT(INTERLEAVEFUNCTION, FUNCTIONPOINT, 196), + + /* Turn on wildcard matching */ + CINIT(WILDCARDMATCH, LONG, 197), + + /* Directory matching callback called before downloading of an + individual file (chunk) started */ + CINIT(CHUNK_BGN_FUNCTION, FUNCTIONPOINT, 198), + + /* Directory matching callback called after the file (chunk) + was downloaded, or skipped */ + CINIT(CHUNK_END_FUNCTION, FUNCTIONPOINT, 199), + + /* Change match (fnmatch-like) callback for wildcard matching */ + CINIT(FNMATCH_FUNCTION, FUNCTIONPOINT, 200), + + /* Let the application define custom chunk data pointer */ + CINIT(CHUNK_DATA, OBJECTPOINT, 201), + + /* FNMATCH_FUNCTION user pointer */ + CINIT(FNMATCH_DATA, OBJECTPOINT, 202), + + /* send linked-list of name:port:address sets */ + CINIT(RESOLVE, OBJECTPOINT, 203), + + /* Set a username for authenticated TLS */ + CINIT(TLSAUTH_USERNAME, STRINGPOINT, 204), + + /* Set a password for authenticated TLS */ + CINIT(TLSAUTH_PASSWORD, STRINGPOINT, 205), + + /* Set authentication type for authenticated TLS */ + CINIT(TLSAUTH_TYPE, STRINGPOINT, 206), + + /* Set to 1 to enable the "TE:" header in HTTP requests to ask for + compressed transfer-encoded responses. Set to 0 to disable the use of TE: + in outgoing requests. The current default is 0, but it might change in a + future libcurl release. + + libcurl will ask for the compressed methods it knows of, and if that + isn't any, it will not ask for transfer-encoding at all even if this + option is set to 1. + + */ + CINIT(TRANSFER_ENCODING, LONG, 207), + + /* Callback function for closing socket (instead of close(2)). The callback + should have type curl_closesocket_callback */ + CINIT(CLOSESOCKETFUNCTION, FUNCTIONPOINT, 208), + CINIT(CLOSESOCKETDATA, OBJECTPOINT, 209), + + /* allow GSSAPI credential delegation */ + CINIT(GSSAPI_DELEGATION, LONG, 210), + + /* Set the name servers to use for DNS resolution */ + CINIT(DNS_SERVERS, STRINGPOINT, 211), + + /* Time-out accept operations (currently for FTP only) after this amount + of milliseconds. */ + CINIT(ACCEPTTIMEOUT_MS, LONG, 212), + + /* Set TCP keepalive */ + CINIT(TCP_KEEPALIVE, LONG, 213), + + /* non-universal keepalive knobs (Linux, AIX, HP-UX, more) */ + CINIT(TCP_KEEPIDLE, LONG, 214), + CINIT(TCP_KEEPINTVL, LONG, 215), + + /* Enable/disable specific SSL features with a bitmask, see CURLSSLOPT_* */ + CINIT(SSL_OPTIONS, LONG, 216), + + /* Set the SMTP auth originator */ + CINIT(MAIL_AUTH, STRINGPOINT, 217), + + /* Enable/disable SASL initial response */ + CINIT(SASL_IR, LONG, 218), + + /* Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_xferinfo_callback + * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */ + CINIT(XFERINFOFUNCTION, FUNCTIONPOINT, 219), + + /* The XOAUTH2 bearer token */ + CINIT(XOAUTH2_BEARER, STRINGPOINT, 220), + + /* Set the interface string to use as outgoing network + * interface for DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_INTERFACE, STRINGPOINT, 221), + + /* Set the local IPv4 address to use for outgoing DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_LOCAL_IP4, STRINGPOINT, 222), + + /* Set the local IPv4 address to use for outgoing DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_LOCAL_IP6, STRINGPOINT, 223), + + /* Set authentication options directly */ + CINIT(LOGIN_OPTIONS, STRINGPOINT, 224), + + /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ + CINIT(SSL_ENABLE_NPN, LONG, 225), + + /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ + CINIT(SSL_ENABLE_ALPN, LONG, 226), + + /* Time to wait for a response to a HTTP request containing an + * Expect: 100-continue header before sending the data anyway. */ + CINIT(EXPECT_100_TIMEOUT_MS, LONG, 227), + + /* This points to a linked list of headers used for proxy requests only, + struct curl_slist kind */ + CINIT(PROXYHEADER, OBJECTPOINT, 228), + + /* Pass in a bitmask of "header options" */ + CINIT(HEADEROPT, LONG, 229), + + /* The public key in DER form used to validate the peer public key + this option is used only if SSL_VERIFYPEER is true */ + CINIT(PINNEDPUBLICKEY, STRINGPOINT, 230), + + /* Path to Unix domain socket */ + CINIT(UNIX_SOCKET_PATH, STRINGPOINT, 231), + + /* Set if we should verify the certificate status. */ + CINIT(SSL_VERIFYSTATUS, LONG, 232), + + /* Set if we should enable TLS false start. */ + CINIT(SSL_FALSESTART, LONG, 233), + + /* Do not squash dot-dot sequences */ + CINIT(PATH_AS_IS, LONG, 234), + + /* Proxy Service Name */ + CINIT(PROXY_SERVICE_NAME, STRINGPOINT, 235), + + /* Service Name */ + CINIT(SERVICE_NAME, STRINGPOINT, 236), + + /* Wait/don't wait for pipe/mutex to clarify */ + CINIT(PIPEWAIT, LONG, 237), + + /* Set the protocol used when curl is given a URL without a protocol */ + CINIT(DEFAULT_PROTOCOL, STRINGPOINT, 238), + + /* Set stream weight, 1 - 256 (default is 16) */ + CINIT(STREAM_WEIGHT, LONG, 239), + + /* Set stream dependency on another CURL handle */ + CINIT(STREAM_DEPENDS, OBJECTPOINT, 240), + + /* Set E-xclusive stream dependency on another CURL handle */ + CINIT(STREAM_DEPENDS_E, OBJECTPOINT, 241), + + /* Do not send any tftp option requests to the server */ + CINIT(TFTP_NO_OPTIONS, LONG, 242), + + /* Linked-list of host:port:connect-to-host:connect-to-port, + overrides the URL's host:port (only for the network layer) */ + CINIT(CONNECT_TO, OBJECTPOINT, 243), + + /* Set TCP Fast Open */ + CINIT(TCP_FASTOPEN, LONG, 244), + + /* Continue to send data if the server responds early with an + * HTTP status code >= 300 */ + CINIT(KEEP_SENDING_ON_ERROR, LONG, 245), + + /* The CApath or CAfile used to validate the proxy certificate + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CINIT(PROXY_CAINFO, STRINGPOINT, 246), + + /* The CApath directory used to validate the proxy certificate + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CINIT(PROXY_CAPATH, STRINGPOINT, 247), + + /* Set if we should verify the proxy in ssl handshake, + set 1 to verify. */ + CINIT(PROXY_SSL_VERIFYPEER, LONG, 248), + + /* Set if we should verify the Common name from the proxy certificate in ssl + * handshake, set 1 to check existence, 2 to ensure that it matches + * the provided hostname. */ + CINIT(PROXY_SSL_VERIFYHOST, LONG, 249), + + /* What version to specifically try to use for proxy. + See CURL_SSLVERSION defines below. */ + CINIT(PROXY_SSLVERSION, LONG, 250), + + /* Set a username for authenticated TLS for proxy */ + CINIT(PROXY_TLSAUTH_USERNAME, STRINGPOINT, 251), + + /* Set a password for authenticated TLS for proxy */ + CINIT(PROXY_TLSAUTH_PASSWORD, STRINGPOINT, 252), + + /* Set authentication type for authenticated TLS for proxy */ + CINIT(PROXY_TLSAUTH_TYPE, STRINGPOINT, 253), + + /* name of the file keeping your private SSL-certificate for proxy */ + CINIT(PROXY_SSLCERT, STRINGPOINT, 254), + + /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") for + proxy */ + CINIT(PROXY_SSLCERTTYPE, STRINGPOINT, 255), + + /* name of the file keeping your private SSL-key for proxy */ + CINIT(PROXY_SSLKEY, STRINGPOINT, 256), + + /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") for + proxy */ + CINIT(PROXY_SSLKEYTYPE, STRINGPOINT, 257), + + /* password for the SSL private key for proxy */ + CINIT(PROXY_KEYPASSWD, STRINGPOINT, 258), + + /* Specify which SSL ciphers to use for proxy */ + CINIT(PROXY_SSL_CIPHER_LIST, STRINGPOINT, 259), + + /* CRL file for proxy */ + CINIT(PROXY_CRLFILE, STRINGPOINT, 260), + + /* Enable/disable specific SSL features with a bitmask for proxy, see + CURLSSLOPT_* */ + CINIT(PROXY_SSL_OPTIONS, LONG, 261), + + /* Name of pre proxy to use. */ + CINIT(PRE_PROXY, STRINGPOINT, 262), + + /* The public key in DER form used to validate the proxy public key + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CINIT(PROXY_PINNEDPUBLICKEY, STRINGPOINT, 263), + + /* Path to an abstract Unix domain socket */ + CINIT(ABSTRACT_UNIX_SOCKET, STRINGPOINT, 264), + + /* Suppress proxy CONNECT response headers from user callbacks */ + CINIT(SUPPRESS_CONNECT_HEADERS, LONG, 265), + + /* The request target, instead of extracted from the URL */ + CINIT(REQUEST_TARGET, STRINGPOINT, 266), + + /* bitmask of allowed auth methods for connections to SOCKS5 proxies */ + CINIT(SOCKS5_AUTH, LONG, 267), + + /* Enable/disable SSH compression */ + CINIT(SSH_COMPRESSION, LONG, 268), + + /* Post MIME data. */ + CINIT(MIMEPOST, OBJECTPOINT, 269), + + CURLOPT_LASTENTRY /* the last unused */ +} CURLoption; + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Backwards compatibility with older names */ +/* These are scheduled to disappear by 2011 */ + +/* This was added in version 7.19.1 */ +#define CURLOPT_POST301 CURLOPT_POSTREDIR + +/* These are scheduled to disappear by 2009 */ + +/* The following were added in 7.17.0 */ +#define CURLOPT_SSLKEYPASSWD CURLOPT_KEYPASSWD +#define CURLOPT_FTPAPPEND CURLOPT_APPEND +#define CURLOPT_FTPLISTONLY CURLOPT_DIRLISTONLY +#define CURLOPT_FTP_SSL CURLOPT_USE_SSL + +/* The following were added earlier */ + +#define CURLOPT_SSLCERTPASSWD CURLOPT_KEYPASSWD +#define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL + +#else +/* This is set if CURL_NO_OLDIES is defined at compile-time */ +#undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */ +#endif + + + /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host + name resolves addresses using more than one IP protocol version, this + option might be handy to force libcurl to use a specific IP version. */ +#define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP + versions that your system allows */ +#define CURL_IPRESOLVE_V4 1 /* resolve to IPv4 addresses */ +#define CURL_IPRESOLVE_V6 2 /* resolve to IPv6 addresses */ + + /* three convenient "aliases" that follow the name scheme better */ +#define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER + + /* These enums are for use with the CURLOPT_HTTP_VERSION option. */ +enum { + CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd + like the library to choose the best possible + for us! */ + CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */ + CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */ + CURL_HTTP_VERSION_2_0, /* please use HTTP 2 in the request */ + CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */ + CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, /* please use HTTP 2 without HTTP/1.1 + Upgrade */ + + CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ +}; + +/* Convenience definition simple because the name of the version is HTTP/2 and + not 2.0. The 2_0 version of the enum name was set while the version was + still planned to be 2.0 and we stick to it for compatibility. */ +#define CURL_HTTP_VERSION_2 CURL_HTTP_VERSION_2_0 + +/* + * Public API enums for RTSP requests + */ +enum { + CURL_RTSPREQ_NONE, /* first in list */ + CURL_RTSPREQ_OPTIONS, + CURL_RTSPREQ_DESCRIBE, + CURL_RTSPREQ_ANNOUNCE, + CURL_RTSPREQ_SETUP, + CURL_RTSPREQ_PLAY, + CURL_RTSPREQ_PAUSE, + CURL_RTSPREQ_TEARDOWN, + CURL_RTSPREQ_GET_PARAMETER, + CURL_RTSPREQ_SET_PARAMETER, + CURL_RTSPREQ_RECORD, + CURL_RTSPREQ_RECEIVE, + CURL_RTSPREQ_LAST /* last in list */ +}; + + /* These enums are for use with the CURLOPT_NETRC option. */ +enum CURL_NETRC_OPTION { + CURL_NETRC_IGNORED, /* The .netrc will never be read. + * This is the default. */ + CURL_NETRC_OPTIONAL, /* A user:password in the URL will be preferred + * to one in the .netrc. */ + CURL_NETRC_REQUIRED, /* A user:password in the URL will be ignored. + * Unless one is set programmatically, the .netrc + * will be queried. */ + CURL_NETRC_LAST +}; + +enum { + CURL_SSLVERSION_DEFAULT, + CURL_SSLVERSION_TLSv1, /* TLS 1.x */ + CURL_SSLVERSION_SSLv2, + CURL_SSLVERSION_SSLv3, + CURL_SSLVERSION_TLSv1_0, + CURL_SSLVERSION_TLSv1_1, + CURL_SSLVERSION_TLSv1_2, + CURL_SSLVERSION_TLSv1_3, + + CURL_SSLVERSION_LAST /* never use, keep last */ +}; + +enum { + CURL_SSLVERSION_MAX_NONE = 0, + CURL_SSLVERSION_MAX_DEFAULT = (CURL_SSLVERSION_TLSv1 << 16), + CURL_SSLVERSION_MAX_TLSv1_0 = (CURL_SSLVERSION_TLSv1_0 << 16), + CURL_SSLVERSION_MAX_TLSv1_1 = (CURL_SSLVERSION_TLSv1_1 << 16), + CURL_SSLVERSION_MAX_TLSv1_2 = (CURL_SSLVERSION_TLSv1_2 << 16), + CURL_SSLVERSION_MAX_TLSv1_3 = (CURL_SSLVERSION_TLSv1_3 << 16), + + /* never use, keep last */ + CURL_SSLVERSION_MAX_LAST = (CURL_SSLVERSION_LAST << 16) +}; + +enum CURL_TLSAUTH { + CURL_TLSAUTH_NONE, + CURL_TLSAUTH_SRP, + CURL_TLSAUTH_LAST /* never use, keep last */ +}; + +/* symbols to use with CURLOPT_POSTREDIR. + CURL_REDIR_POST_301, CURL_REDIR_POST_302 and CURL_REDIR_POST_303 + can be bitwise ORed so that CURL_REDIR_POST_301 | CURL_REDIR_POST_302 + | CURL_REDIR_POST_303 == CURL_REDIR_POST_ALL */ + +#define CURL_REDIR_GET_ALL 0 +#define CURL_REDIR_POST_301 1 +#define CURL_REDIR_POST_302 2 +#define CURL_REDIR_POST_303 4 +#define CURL_REDIR_POST_ALL \ + (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303) + +typedef enum { + CURL_TIMECOND_NONE, + + CURL_TIMECOND_IFMODSINCE, + CURL_TIMECOND_IFUNMODSINCE, + CURL_TIMECOND_LASTMOD, + + CURL_TIMECOND_LAST +} curl_TimeCond; + +/* Special size_t value signaling a zero-terminated string. */ +#define CURL_ZERO_TERMINATED ((size_t) -1) + +/* curl_strequal() and curl_strnequal() are subject for removal in a future + release */ +CURL_EXTERN int curl_strequal(const char *s1, const char *s2); +CURL_EXTERN int curl_strnequal(const char *s1, const char *s2, size_t n); + +/* Mime/form handling support. */ +typedef struct curl_mime_s curl_mime; /* Mime context. */ +typedef struct curl_mimepart_s curl_mimepart; /* Mime part context. */ + +/* + * NAME curl_mime_init() + * + * DESCRIPTION + * + * Create a mime context and return its handle. The easy parameter is the + * target handle. + */ +CURL_EXTERN curl_mime *curl_mime_init(CURL *easy); + +/* + * NAME curl_mime_free() + * + * DESCRIPTION + * + * release a mime handle and its substructures. + */ +CURL_EXTERN void curl_mime_free(curl_mime *mime); + +/* + * NAME curl_mime_addpart() + * + * DESCRIPTION + * + * Append a new empty part to the given mime context and return a handle to + * the created part. + */ +CURL_EXTERN curl_mimepart *curl_mime_addpart(curl_mime *mime); + +/* + * NAME curl_mime_name() + * + * DESCRIPTION + * + * Set mime/form part name. + */ +CURL_EXTERN CURLcode curl_mime_name(curl_mimepart *part, const char *name); + +/* + * NAME curl_mime_filename() + * + * DESCRIPTION + * + * Set mime part remote file name. + */ +CURL_EXTERN CURLcode curl_mime_filename(curl_mimepart *part, + const char *filename); + +/* + * NAME curl_mime_type() + * + * DESCRIPTION + * + * Set mime part type. + */ +CURL_EXTERN CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype); + +/* + * NAME curl_mime_encoder() + * + * DESCRIPTION + * + * Set mime data transfer encoder. + */ +CURL_EXTERN CURLcode curl_mime_encoder(curl_mimepart *part, + const char *encoding); + +/* + * NAME curl_mime_data() + * + * DESCRIPTION + * + * Set mime part data source from memory data, + */ +CURL_EXTERN CURLcode curl_mime_data(curl_mimepart *part, + const char *data, size_t datasize); + +/* + * NAME curl_mime_filedata() + * + * DESCRIPTION + * + * Set mime part data source from named file. + */ +CURL_EXTERN CURLcode curl_mime_filedata(curl_mimepart *part, + const char *filename); + +/* + * NAME curl_mime_data_cb() + * + * DESCRIPTION + * + * Set mime part data source from callback function. + */ +CURL_EXTERN CURLcode curl_mime_data_cb(curl_mimepart *part, + curl_off_t datasize, + curl_read_callback readfunc, + curl_seek_callback seekfunc, + curl_free_callback freefunc, + void *arg); + +/* + * NAME curl_mime_subparts() + * + * DESCRIPTION + * + * Set mime part data source from subparts. + */ +CURL_EXTERN CURLcode curl_mime_subparts(curl_mimepart *part, + curl_mime *subparts); +/* + * NAME curl_mime_headers() + * + * DESCRIPTION + * + * Set mime part headers. + */ +CURL_EXTERN CURLcode curl_mime_headers(curl_mimepart *part, + struct curl_slist *headers, + int take_ownership); + +/* Old form API. */ +/* name is uppercase CURLFORM_ */ +#ifdef CFINIT +#undef CFINIT +#endif + +#ifdef CURL_ISOCPP +#define CFINIT(name) CURLFORM_ ## name +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define CFINIT(name) CURLFORM_/**/name +#endif + +typedef enum { + CFINIT(NOTHING), /********* the first one is unused ************/ + + /* */ + CFINIT(COPYNAME), + CFINIT(PTRNAME), + CFINIT(NAMELENGTH), + CFINIT(COPYCONTENTS), + CFINIT(PTRCONTENTS), + CFINIT(CONTENTSLENGTH), + CFINIT(FILECONTENT), + CFINIT(ARRAY), + CFINIT(OBSOLETE), + CFINIT(FILE), + + CFINIT(BUFFER), + CFINIT(BUFFERPTR), + CFINIT(BUFFERLENGTH), + + CFINIT(CONTENTTYPE), + CFINIT(CONTENTHEADER), + CFINIT(FILENAME), + CFINIT(END), + CFINIT(OBSOLETE2), + + CFINIT(STREAM), + CFINIT(CONTENTLEN), /* added in 7.46.0, provide a curl_off_t length */ + + CURLFORM_LASTENTRY /* the last unused */ +} CURLformoption; + +#undef CFINIT /* done */ + +/* structure to be used as parameter for CURLFORM_ARRAY */ +struct curl_forms { + CURLformoption option; + const char *value; +}; + +/* use this for multipart formpost building */ +/* Returns code for curl_formadd() + * + * Returns: + * CURL_FORMADD_OK on success + * CURL_FORMADD_MEMORY if the FormInfo allocation fails + * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form + * CURL_FORMADD_NULL if a null pointer was given for a char + * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed + * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used + * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) + * CURL_FORMADD_MEMORY if a curl_httppost struct cannot be allocated + * CURL_FORMADD_MEMORY if some allocation for string copying failed. + * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array + * + ***************************************************************************/ +typedef enum { + CURL_FORMADD_OK, /* first, no error */ + + CURL_FORMADD_MEMORY, + CURL_FORMADD_OPTION_TWICE, + CURL_FORMADD_NULL, + CURL_FORMADD_UNKNOWN_OPTION, + CURL_FORMADD_INCOMPLETE, + CURL_FORMADD_ILLEGAL_ARRAY, + CURL_FORMADD_DISABLED, /* libcurl was built with this disabled */ + + CURL_FORMADD_LAST /* last */ +} CURLFORMcode; + +/* + * NAME curl_formadd() + * + * DESCRIPTION + * + * Pretty advanced function for building multi-part formposts. Each invoke + * adds one part that together construct a full post. Then use + * CURLOPT_HTTPPOST to send it off to libcurl. + */ +CURL_EXTERN CURLFORMcode curl_formadd(struct curl_httppost **httppost, + struct curl_httppost **last_post, + ...); + +/* + * callback function for curl_formget() + * The void *arg pointer will be the one passed as second argument to + * curl_formget(). + * The character buffer passed to it must not be freed. + * Should return the buffer length passed to it as the argument "len" on + * success. + */ +typedef size_t (*curl_formget_callback)(void *arg, const char *buf, + size_t len); + +/* + * NAME curl_formget() + * + * DESCRIPTION + * + * Serialize a curl_httppost struct built with curl_formadd(). + * Accepts a void pointer as second argument which will be passed to + * the curl_formget_callback function. + * Returns 0 on success. + */ +CURL_EXTERN int curl_formget(struct curl_httppost *form, void *arg, + curl_formget_callback append); +/* + * NAME curl_formfree() + * + * DESCRIPTION + * + * Free a multipart formpost previously built with curl_formadd(). + */ +CURL_EXTERN void curl_formfree(struct curl_httppost *form); + +/* + * NAME curl_getenv() + * + * DESCRIPTION + * + * Returns a malloc()'ed string that MUST be curl_free()ed after usage is + * complete. DEPRECATED - see lib/README.curlx + */ +CURL_EXTERN char *curl_getenv(const char *variable); + +/* + * NAME curl_version() + * + * DESCRIPTION + * + * Returns a static ascii string of the libcurl version. + */ +CURL_EXTERN char *curl_version(void); + +/* + * NAME curl_easy_escape() + * + * DESCRIPTION + * + * Escapes URL strings (converts all letters consider illegal in URLs to their + * %XX versions). This function returns a new allocated string or NULL if an + * error occurred. + */ +CURL_EXTERN char *curl_easy_escape(CURL *handle, + const char *string, + int length); + +/* the previous version: */ +CURL_EXTERN char *curl_escape(const char *string, + int length); + + +/* + * NAME curl_easy_unescape() + * + * DESCRIPTION + * + * Unescapes URL encoding in strings (converts all %XX codes to their 8bit + * versions). This function returns a new allocated string or NULL if an error + * occurred. + * Conversion Note: On non-ASCII platforms the ASCII %XX codes are + * converted into the host encoding. + */ +CURL_EXTERN char *curl_easy_unescape(CURL *handle, + const char *string, + int length, + int *outlength); + +/* the previous version */ +CURL_EXTERN char *curl_unescape(const char *string, + int length); + +/* + * NAME curl_free() + * + * DESCRIPTION + * + * Provided for de-allocation in the same translation unit that did the + * allocation. Added in libcurl 7.10 + */ +CURL_EXTERN void curl_free(void *p); + +/* + * NAME curl_global_init() + * + * DESCRIPTION + * + * curl_global_init() should be invoked exactly once for each application that + * uses libcurl and before any call of other libcurl functions. + * + * This function is not thread-safe! + */ +CURL_EXTERN CURLcode curl_global_init(long flags); + +/* + * NAME curl_global_init_mem() + * + * DESCRIPTION + * + * curl_global_init() or curl_global_init_mem() should be invoked exactly once + * for each application that uses libcurl. This function can be used to + * initialize libcurl and set user defined memory management callback + * functions. Users can implement memory management routines to check for + * memory leaks, check for mis-use of the curl library etc. User registered + * callback routines with be invoked by this library instead of the system + * memory management routines like malloc, free etc. + */ +CURL_EXTERN CURLcode curl_global_init_mem(long flags, + curl_malloc_callback m, + curl_free_callback f, + curl_realloc_callback r, + curl_strdup_callback s, + curl_calloc_callback c); + +/* + * NAME curl_global_cleanup() + * + * DESCRIPTION + * + * curl_global_cleanup() should be invoked exactly once for each application + * that uses libcurl + */ +CURL_EXTERN void curl_global_cleanup(void); + +/* linked-list structure for the CURLOPT_QUOTE option (and other) */ +struct curl_slist { + char *data; + struct curl_slist *next; +}; + +/* + * NAME curl_global_sslset() + * + * DESCRIPTION + * + * When built with multiple SSL backends, curl_global_sslset() allows to + * choose one. This function can only be called once, and it must be called + * *before* curl_global_init(). + * + * The backend can be identified by the id (e.g. CURLSSLBACKEND_OPENSSL). The + * backend can also be specified via the name parameter (passing -1 as id). + * If both id and name are specified, the name will be ignored. If neither id + * nor name are specified, the function will fail with + * CURLSSLSET_UNKNOWN_BACKEND and set the "avail" pointer to the + * NULL-terminated list of available backends. + * + * Upon success, the function returns CURLSSLSET_OK. + * + * If the specified SSL backend is not available, the function returns + * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a NULL-terminated + * list of available SSL backends. + * + * The SSL backend can be set only once. If it has already been set, a + * subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE. + */ + +typedef struct { + curl_sslbackend id; + const char *name; +} curl_ssl_backend; + +typedef enum { + CURLSSLSET_OK = 0, + CURLSSLSET_UNKNOWN_BACKEND, + CURLSSLSET_TOO_LATE, + CURLSSLSET_NO_BACKENDS /* libcurl was built without any SSL support */ +} CURLsslset; + +CURL_EXTERN CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, + const curl_ssl_backend ***avail); + +/* + * NAME curl_slist_append() + * + * DESCRIPTION + * + * Appends a string to a linked list. If no list exists, it will be created + * first. Returns the new list, after appending. + */ +CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *, + const char *); + +/* + * NAME curl_slist_free_all() + * + * DESCRIPTION + * + * free a previously built curl_slist. + */ +CURL_EXTERN void curl_slist_free_all(struct curl_slist *); + +/* + * NAME curl_getdate() + * + * DESCRIPTION + * + * Returns the time, in seconds since 1 Jan 1970 of the time string given in + * the first argument. The time argument in the second parameter is unused + * and should be set to NULL. + */ +CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); + +/* info about the certificate chain, only for OpenSSL builds. Asked + for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ +struct curl_certinfo { + int num_of_certs; /* number of certificates with information */ + struct curl_slist **certinfo; /* for each index in this array, there's a + linked list with textual information in the + format "name: value" */ +}; + +/* Information about the SSL library used and the respective internal SSL + handle, which can be used to obtain further information regarding the + connection. Asked for with CURLINFO_TLS_SSL_PTR or CURLINFO_TLS_SESSION. */ +struct curl_tlssessioninfo { + curl_sslbackend backend; + void *internals; +}; + +#define CURLINFO_STRING 0x100000 +#define CURLINFO_LONG 0x200000 +#define CURLINFO_DOUBLE 0x300000 +#define CURLINFO_SLIST 0x400000 +#define CURLINFO_PTR 0x400000 /* same as SLIST */ +#define CURLINFO_SOCKET 0x500000 +#define CURLINFO_OFF_T 0x600000 +#define CURLINFO_MASK 0x0fffff +#define CURLINFO_TYPEMASK 0xf00000 + +typedef enum { + CURLINFO_NONE, /* first, never use this */ + CURLINFO_EFFECTIVE_URL = CURLINFO_STRING + 1, + CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2, + CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE + 3, + CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE + 4, + CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE + 5, + CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6, + CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE + 7, + CURLINFO_SIZE_UPLOAD_T = CURLINFO_OFF_T + 7, + CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE + 8, + CURLINFO_SIZE_DOWNLOAD_T = CURLINFO_OFF_T + 8, + CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE + 9, + CURLINFO_SPEED_DOWNLOAD_T = CURLINFO_OFF_T + 9, + CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE + 10, + CURLINFO_SPEED_UPLOAD_T = CURLINFO_OFF_T + 10, + CURLINFO_HEADER_SIZE = CURLINFO_LONG + 11, + CURLINFO_REQUEST_SIZE = CURLINFO_LONG + 12, + CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13, + CURLINFO_FILETIME = CURLINFO_LONG + 14, + CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15, + CURLINFO_CONTENT_LENGTH_DOWNLOAD_T = CURLINFO_OFF_T + 15, + CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE + 16, + CURLINFO_CONTENT_LENGTH_UPLOAD_T = CURLINFO_OFF_T + 16, + CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17, + CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18, + CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE + 19, + CURLINFO_REDIRECT_COUNT = CURLINFO_LONG + 20, + CURLINFO_PRIVATE = CURLINFO_STRING + 21, + CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG + 22, + CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG + 23, + CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG + 24, + CURLINFO_OS_ERRNO = CURLINFO_LONG + 25, + CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26, + CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27, + CURLINFO_COOKIELIST = CURLINFO_SLIST + 28, + CURLINFO_LASTSOCKET = CURLINFO_LONG + 29, + CURLINFO_FTP_ENTRY_PATH = CURLINFO_STRING + 30, + CURLINFO_REDIRECT_URL = CURLINFO_STRING + 31, + CURLINFO_PRIMARY_IP = CURLINFO_STRING + 32, + CURLINFO_APPCONNECT_TIME = CURLINFO_DOUBLE + 33, + CURLINFO_CERTINFO = CURLINFO_PTR + 34, + CURLINFO_CONDITION_UNMET = CURLINFO_LONG + 35, + CURLINFO_RTSP_SESSION_ID = CURLINFO_STRING + 36, + CURLINFO_RTSP_CLIENT_CSEQ = CURLINFO_LONG + 37, + CURLINFO_RTSP_SERVER_CSEQ = CURLINFO_LONG + 38, + CURLINFO_RTSP_CSEQ_RECV = CURLINFO_LONG + 39, + CURLINFO_PRIMARY_PORT = CURLINFO_LONG + 40, + CURLINFO_LOCAL_IP = CURLINFO_STRING + 41, + CURLINFO_LOCAL_PORT = CURLINFO_LONG + 42, + CURLINFO_TLS_SESSION = CURLINFO_PTR + 43, + CURLINFO_ACTIVESOCKET = CURLINFO_SOCKET + 44, + CURLINFO_TLS_SSL_PTR = CURLINFO_PTR + 45, + CURLINFO_HTTP_VERSION = CURLINFO_LONG + 46, + CURLINFO_PROXY_SSL_VERIFYRESULT = CURLINFO_LONG + 47, + CURLINFO_PROTOCOL = CURLINFO_LONG + 48, + CURLINFO_SCHEME = CURLINFO_STRING + 49, + /* Fill in new entries below here! */ + + CURLINFO_LASTONE = 49 +} CURLINFO; + +/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as + CURLINFO_HTTP_CODE */ +#define CURLINFO_HTTP_CODE CURLINFO_RESPONSE_CODE + +typedef enum { + CURLCLOSEPOLICY_NONE, /* first, never use this */ + + CURLCLOSEPOLICY_OLDEST, + CURLCLOSEPOLICY_LEAST_RECENTLY_USED, + CURLCLOSEPOLICY_LEAST_TRAFFIC, + CURLCLOSEPOLICY_SLOWEST, + CURLCLOSEPOLICY_CALLBACK, + + CURLCLOSEPOLICY_LAST /* last, never use this */ +} curl_closepolicy; + +#define CURL_GLOBAL_SSL (1<<0) /* no purpose since since 7.57.0 */ +#define CURL_GLOBAL_WIN32 (1<<1) +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) +#define CURL_GLOBAL_NOTHING 0 +#define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL +#define CURL_GLOBAL_ACK_EINTR (1<<2) + + +/***************************************************************************** + * Setup defines, protos etc for the sharing stuff. + */ + +/* Different data locks for a single share */ +typedef enum { + CURL_LOCK_DATA_NONE = 0, + /* CURL_LOCK_DATA_SHARE is used internally to say that + * the locking is just made to change the internal state of the share + * itself. + */ + CURL_LOCK_DATA_SHARE, + CURL_LOCK_DATA_COOKIE, + CURL_LOCK_DATA_DNS, + CURL_LOCK_DATA_SSL_SESSION, + CURL_LOCK_DATA_CONNECT, + CURL_LOCK_DATA_LAST +} curl_lock_data; + +/* Different lock access types */ +typedef enum { + CURL_LOCK_ACCESS_NONE = 0, /* unspecified action */ + CURL_LOCK_ACCESS_SHARED = 1, /* for read perhaps */ + CURL_LOCK_ACCESS_SINGLE = 2, /* for write perhaps */ + CURL_LOCK_ACCESS_LAST /* never use */ +} curl_lock_access; + +typedef void (*curl_lock_function)(CURL *handle, + curl_lock_data data, + curl_lock_access locktype, + void *userptr); +typedef void (*curl_unlock_function)(CURL *handle, + curl_lock_data data, + void *userptr); + + +typedef enum { + CURLSHE_OK, /* all is fine */ + CURLSHE_BAD_OPTION, /* 1 */ + CURLSHE_IN_USE, /* 2 */ + CURLSHE_INVALID, /* 3 */ + CURLSHE_NOMEM, /* 4 out of memory */ + CURLSHE_NOT_BUILT_IN, /* 5 feature not present in lib */ + CURLSHE_LAST /* never use */ +} CURLSHcode; + +typedef enum { + CURLSHOPT_NONE, /* don't use */ + CURLSHOPT_SHARE, /* specify a data type to share */ + CURLSHOPT_UNSHARE, /* specify which data type to stop sharing */ + CURLSHOPT_LOCKFUNC, /* pass in a 'curl_lock_function' pointer */ + CURLSHOPT_UNLOCKFUNC, /* pass in a 'curl_unlock_function' pointer */ + CURLSHOPT_USERDATA, /* pass in a user data pointer used in the lock/unlock + callback functions */ + CURLSHOPT_LAST /* never use */ +} CURLSHoption; + +CURL_EXTERN CURLSH *curl_share_init(void); +CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...); +CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *); + +/**************************************************************************** + * Structures for querying information about the curl library at runtime. + */ + +typedef enum { + CURLVERSION_FIRST, + CURLVERSION_SECOND, + CURLVERSION_THIRD, + CURLVERSION_FOURTH, + CURLVERSION_FIFTH, + CURLVERSION_LAST /* never actually use this */ +} CURLversion; + +/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by + basically all programs ever that want to get version information. It is + meant to be a built-in version number for what kind of struct the caller + expects. If the struct ever changes, we redefine the NOW to another enum + from above. */ +#define CURLVERSION_NOW CURLVERSION_FIFTH + +typedef struct { + CURLversion age; /* age of the returned struct */ + const char *version; /* LIBCURL_VERSION */ + unsigned int version_num; /* LIBCURL_VERSION_NUM */ + const char *host; /* OS/host/cpu/machine when configured */ + int features; /* bitmask, see defines below */ + const char *ssl_version; /* human readable string */ + long ssl_version_num; /* not used anymore, always 0 */ + const char *libz_version; /* human readable string */ + /* protocols is terminated by an entry with a NULL protoname */ + const char * const *protocols; + + /* The fields below this were added in CURLVERSION_SECOND */ + const char *ares; + int ares_num; + + /* This field was added in CURLVERSION_THIRD */ + const char *libidn; + + /* These field were added in CURLVERSION_FOURTH */ + + /* Same as '_libiconv_version' if built with HAVE_ICONV */ + int iconv_ver_num; + + const char *libssh_version; /* human readable string */ + + /* These fields were added in CURLVERSION_FIFTH */ + + unsigned int brotli_ver_num; /* Numeric Brotli version + (MAJOR << 24) | (MINOR << 12) | PATCH */ + const char *brotli_version; /* human readable string. */ + +} curl_version_info_data; + +#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ +#define CURL_VERSION_KERBEROS4 (1<<1) /* Kerberos V4 auth is supported + (deprecated) */ +#define CURL_VERSION_SSL (1<<2) /* SSL options are present */ +#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */ +#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */ +#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth is supported + (deprecated) */ +#define CURL_VERSION_DEBUG (1<<6) /* Built with debug capabilities */ +#define CURL_VERSION_ASYNCHDNS (1<<7) /* Asynchronous DNS resolves */ +#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth is supported */ +#define CURL_VERSION_LARGEFILE (1<<9) /* Supports files larger than 2GB */ +#define CURL_VERSION_IDN (1<<10) /* Internationized Domain Names are + supported */ +#define CURL_VERSION_SSPI (1<<11) /* Built against Windows SSPI */ +#define CURL_VERSION_CONV (1<<12) /* Character conversions supported */ +#define CURL_VERSION_CURLDEBUG (1<<13) /* Debug memory tracking supported */ +#define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ +#define CURL_VERSION_NTLM_WB (1<<15) /* NTLM delegation to winbind helper + is supported */ +#define CURL_VERSION_HTTP2 (1<<16) /* HTTP2 support built-in */ +#define CURL_VERSION_GSSAPI (1<<17) /* Built against a GSS-API library */ +#define CURL_VERSION_KERBEROS5 (1<<18) /* Kerberos V5 auth is supported */ +#define CURL_VERSION_UNIX_SOCKETS (1<<19) /* Unix domain sockets support */ +#define CURL_VERSION_PSL (1<<20) /* Mozilla's Public Suffix List, used + for cookie domain verification */ +#define CURL_VERSION_HTTPS_PROXY (1<<21) /* HTTPS-proxy support built-in */ +#define CURL_VERSION_MULTI_SSL (1<<22) /* Multiple SSL backends available */ +#define CURL_VERSION_BROTLI (1<<23) /* Brotli features are present. */ + + /* + * NAME curl_version_info() + * + * DESCRIPTION + * + * This function returns a pointer to a static copy of the version info + * struct. See above. + */ +CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion); + +/* + * NAME curl_easy_strerror() + * + * DESCRIPTION + * + * The curl_easy_strerror function may be used to turn a CURLcode value + * into the equivalent human readable error string. This is useful + * for printing meaningful error messages. + */ +CURL_EXTERN const char *curl_easy_strerror(CURLcode); + +/* + * NAME curl_share_strerror() + * + * DESCRIPTION + * + * The curl_share_strerror function may be used to turn a CURLSHcode value + * into the equivalent human readable error string. This is useful + * for printing meaningful error messages. + */ +CURL_EXTERN const char *curl_share_strerror(CURLSHcode); + +/* + * NAME curl_easy_pause() + * + * DESCRIPTION + * + * The curl_easy_pause function pauses or unpauses transfers. Select the new + * state by setting the bitmask, use the convenience defines below. + * + */ +CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); + +#define CURLPAUSE_RECV (1<<0) +#define CURLPAUSE_RECV_CONT (0) + +#define CURLPAUSE_SEND (1<<2) +#define CURLPAUSE_SEND_CONT (0) + +#define CURLPAUSE_ALL (CURLPAUSE_RECV|CURLPAUSE_SEND) +#define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT) + +#ifdef __cplusplus +} +#endif + +/* unfortunately, the easy.h and multi.h include files need options and info + stuff before they can be included! */ +#include "easy.h" /* nothing in curl is fun without the easy stuff */ +#include "multi.h" + +/* the typechecker doesn't work in C++ (yet) */ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \ + ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && \ + !defined(__cplusplus) && !defined(CURL_DISABLE_TYPECHECK) +#include "typecheck-gcc.h" +#else +#if defined(__STDC__) && (__STDC__ >= 1) +/* This preprocessor magic that replaces a call with the exact same call is + only done to make sure application authors pass exactly three arguments + to these functions. */ +#define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param) +#define curl_easy_getinfo(handle,info,arg) curl_easy_getinfo(handle,info,arg) +#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) +#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) +#endif /* __STDC__ >= 1 */ +#endif /* gcc >= 4.3 && !__cplusplus */ + +#endif /* __CURL_CURL_H */ diff --git a/ext/curl-7.58.0/Win32/include/curl/curlver.h b/ext/curl-7.58.0/Win32/include/curl/curlver.h new file mode 100644 index 000000000..6d93cc118 --- /dev/null +++ b/ext/curl-7.58.0/Win32/include/curl/curlver.h @@ -0,0 +1,77 @@ +#ifndef __CURL_CURLVER_H +#define __CURL_CURLVER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* This header file contains nothing but libcurl version info, generated by + a script at release-time. This was made its own header file in 7.11.2 */ + +/* This is the global package copyright */ +#define LIBCURL_COPYRIGHT "1996 - 2017 Daniel Stenberg, ." + +/* This is the version number of the libcurl package from which this header + file origins: */ +#define LIBCURL_VERSION "7.58.0" + +/* The numeric version number is also available "in parts" by using these + defines: */ +#define LIBCURL_VERSION_MAJOR 7 +#define LIBCURL_VERSION_MINOR 58 +#define LIBCURL_VERSION_PATCH 0 + +/* This is the numeric version of the libcurl version number, meant for easier + parsing and comparions by programs. The LIBCURL_VERSION_NUM define will + always follow this syntax: + + 0xXXYYZZ + + Where XX, YY and ZZ are the main version, release and patch numbers in + hexadecimal (using 8 bits each). All three numbers are always represented + using two digits. 1.2 would appear as "0x010200" while version 9.11.7 + appears as "0x090b07". + + This 6-digit (24 bits) hexadecimal number does not show pre-release number, + and it is always a greater number in a more recent release. It makes + comparisons with greater than and less than work. + + Note: This define is the full hex number and _does not_ use the + CURL_VERSION_BITS() macro since curl's own configure script greps for it + and needs it to contain the full number. +*/ +#define LIBCURL_VERSION_NUM 0x073a00 + +/* + * This is the date and time when the full source package was created. The + * timestamp is not stored in git, as the timestamp is properly set in the + * tarballs by the maketgz script. + * + * The format of the date follows this template: + * + * "2007-11-23" + */ +#define LIBCURL_TIMESTAMP "2018-01-24" + +#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|z) +#define CURL_AT_LEAST_VERSION(x,y,z) \ + (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z)) + +#endif /* __CURL_CURLVER_H */ diff --git a/ext/curl-7.58.0/Win32/include/curl/easy.h b/ext/curl-7.58.0/Win32/include/curl/easy.h new file mode 100644 index 000000000..752c5049f --- /dev/null +++ b/ext/curl-7.58.0/Win32/include/curl/easy.h @@ -0,0 +1,102 @@ +#ifndef __CURL_EASY_H +#define __CURL_EASY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +CURL_EXTERN CURL *curl_easy_init(void); +CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); +CURL_EXTERN CURLcode curl_easy_perform(CURL *curl); +CURL_EXTERN void curl_easy_cleanup(CURL *curl); + +/* + * NAME curl_easy_getinfo() + * + * DESCRIPTION + * + * Request internal information from the curl session with this function. The + * third argument MUST be a pointer to a long, a pointer to a char * or a + * pointer to a double (as the documentation describes elsewhere). The data + * pointed to will be filled in accordingly and can be relied upon only if the + * function returns CURLE_OK. This function is intended to get used *AFTER* a + * performed transfer, all results from this function are undefined until the + * transfer is completed. + */ +CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); + + +/* + * NAME curl_easy_duphandle() + * + * DESCRIPTION + * + * Creates a new curl session handle with the same options set for the handle + * passed in. Duplicating a handle could only be a matter of cloning data and + * options, internal state info and things like persistent connections cannot + * be transferred. It is useful in multithreaded applications when you can run + * curl_easy_duphandle() for each new thread to avoid a series of identical + * curl_easy_setopt() invokes in every thread. + */ +CURL_EXTERN CURL *curl_easy_duphandle(CURL *curl); + +/* + * NAME curl_easy_reset() + * + * DESCRIPTION + * + * Re-initializes a CURL handle to the default values. This puts back the + * handle to the same state as it was in when it was just created. + * + * It does keep: live connections, the Session ID cache, the DNS cache and the + * cookies. + */ +CURL_EXTERN void curl_easy_reset(CURL *curl); + +/* + * NAME curl_easy_recv() + * + * DESCRIPTION + * + * Receives data from the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, + size_t *n); + +/* + * NAME curl_easy_send() + * + * DESCRIPTION + * + * Sends data over the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer, + size_t buflen, size_t *n); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/curl-7.58.0/Win32/include/curl/mprintf.h b/ext/curl-7.58.0/Win32/include/curl/mprintf.h new file mode 100644 index 000000000..e20f546e1 --- /dev/null +++ b/ext/curl-7.58.0/Win32/include/curl/mprintf.h @@ -0,0 +1,50 @@ +#ifndef __CURL_MPRINTF_H +#define __CURL_MPRINTF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include +#include /* needed for FILE */ +#include "curl.h" /* for CURL_EXTERN */ + +#ifdef __cplusplus +extern "C" { +#endif + +CURL_EXTERN int curl_mprintf(const char *format, ...); +CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...); +CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...); +CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength, + const char *format, ...); +CURL_EXTERN int curl_mvprintf(const char *format, va_list args); +CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args); +CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args); +CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength, + const char *format, va_list args); +CURL_EXTERN char *curl_maprintf(const char *format, ...); +CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); + +#ifdef __cplusplus +} +#endif + +#endif /* __CURL_MPRINTF_H */ diff --git a/ext/curl-7.58.0/Win32/include/curl/multi.h b/ext/curl-7.58.0/Win32/include/curl/multi.h new file mode 100644 index 000000000..911c91dd1 --- /dev/null +++ b/ext/curl-7.58.0/Win32/include/curl/multi.h @@ -0,0 +1,439 @@ +#ifndef __CURL_MULTI_H +#define __CURL_MULTI_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +/* + This is an "external" header file. Don't give away any internals here! + + GOALS + + o Enable a "pull" interface. The application that uses libcurl decides where + and when to ask libcurl to get/send data. + + o Enable multiple simultaneous transfers in the same thread without making it + complicated for the application. + + o Enable the application to select() on its own file descriptors and curl's + file descriptors simultaneous easily. + +*/ + +/* + * This header file should not really need to include "curl.h" since curl.h + * itself includes this file and we expect user applications to do #include + * without the need for especially including multi.h. + * + * For some reason we added this include here at one point, and rather than to + * break existing (wrongly written) libcurl applications, we leave it as-is + * but with this warning attached. + */ +#include "curl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER) +typedef struct Curl_multi CURLM; +#else +typedef void CURLM; +#endif + +typedef enum { + CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or + curl_multi_socket*() soon */ + CURLM_OK, + CURLM_BAD_HANDLE, /* the passed-in handle is not a valid CURLM handle */ + CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */ + CURLM_OUT_OF_MEMORY, /* if you ever get this, you're in deep sh*t */ + CURLM_INTERNAL_ERROR, /* this is a libcurl bug */ + CURLM_BAD_SOCKET, /* the passed in socket argument did not match */ + CURLM_UNKNOWN_OPTION, /* curl_multi_setopt() with unsupported option */ + CURLM_ADDED_ALREADY, /* an easy handle already added to a multi handle was + attempted to get added - again */ + CURLM_LAST +} CURLMcode; + +/* just to make code nicer when using curl_multi_socket() you can now check + for CURLM_CALL_MULTI_SOCKET too in the same style it works for + curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ +#define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM + +/* bitmask bits for CURLMOPT_PIPELINING */ +#define CURLPIPE_NOTHING 0L +#define CURLPIPE_HTTP1 1L +#define CURLPIPE_MULTIPLEX 2L + +typedef enum { + CURLMSG_NONE, /* first, not used */ + CURLMSG_DONE, /* This easy handle has completed. 'result' contains + the CURLcode of the transfer */ + CURLMSG_LAST /* last, not used */ +} CURLMSG; + +struct CURLMsg { + CURLMSG msg; /* what this message means */ + CURL *easy_handle; /* the handle it concerns */ + union { + void *whatever; /* message-specific data */ + CURLcode result; /* return code for transfer */ + } data; +}; +typedef struct CURLMsg CURLMsg; + +/* Based on poll(2) structure and values. + * We don't use pollfd and POLL* constants explicitly + * to cover platforms without poll(). */ +#define CURL_WAIT_POLLIN 0x0001 +#define CURL_WAIT_POLLPRI 0x0002 +#define CURL_WAIT_POLLOUT 0x0004 + +struct curl_waitfd { + curl_socket_t fd; + short events; + short revents; /* not supported yet */ +}; + +/* + * Name: curl_multi_init() + * + * Desc: inititalize multi-style curl usage + * + * Returns: a new CURLM handle to use in all 'curl_multi' functions. + */ +CURL_EXTERN CURLM *curl_multi_init(void); + +/* + * Name: curl_multi_add_handle() + * + * Desc: add a standard curl handle to the multi stack + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_remove_handle() + * + * Desc: removes a curl handle from the multi stack again + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_fdset() + * + * Desc: Ask curl for its fd_set sets. The app can use these to select() or + * poll() on. We want curl_multi_perform() called as soon as one of + * them are ready. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); + +/* + * Name: curl_multi_wait() + * + * Desc: Poll on all fds within a CURLM set as well as any + * additional fds passed to the function. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + int *ret); + + /* + * Name: curl_multi_perform() + * + * Desc: When the app thinks there's data available for curl it calls this + * function to read/write whatever there is right now. This returns + * as soon as the reads and writes are done. This function does not + * require that there actually is data available for reading or that + * data can be written, it can be called just in case. It returns + * the number of handles that still transfer data in the second + * argument's integer-pointer. + * + * Returns: CURLMcode type, general multi error code. *NOTE* that this only + * returns errors etc regarding the whole multi stack. There might + * still have occurred problems on invidual transfers even when this + * returns OK. + */ +CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle, + int *running_handles); + + /* + * Name: curl_multi_cleanup() + * + * Desc: Cleans up and removes a whole multi stack. It does not free or + * touch any individual easy handles in any way. We need to define + * in what state those handles will be if this function is called + * in the middle of a transfer. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); + +/* + * Name: curl_multi_info_read() + * + * Desc: Ask the multi handle if there's any messages/informationals from + * the individual transfers. Messages include informationals such as + * error code from the transfer or just the fact that a transfer is + * completed. More details on these should be written down as well. + * + * Repeated calls to this function will return a new struct each + * time, until a special "end of msgs" struct is returned as a signal + * that there is no more to get at this point. + * + * The data the returned pointer points to will not survive calling + * curl_multi_cleanup(). + * + * The 'CURLMsg' struct is meant to be very simple and only contain + * very basic information. If more involved information is wanted, + * we will provide the particular "transfer handle" in that struct + * and that should/could/would be used in subsequent + * curl_easy_getinfo() calls (or similar). The point being that we + * must never expose complex structs to applications, as then we'll + * undoubtably get backwards compatibility problems in the future. + * + * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out + * of structs. It also writes the number of messages left in the + * queue (after this read) in the integer the second argument points + * to. + */ +CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, + int *msgs_in_queue); + +/* + * Name: curl_multi_strerror() + * + * Desc: The curl_multi_strerror function may be used to turn a CURLMcode + * value into the equivalent human readable error string. This is + * useful for printing meaningful error messages. + * + * Returns: A pointer to a zero-terminated error message. + */ +CURL_EXTERN const char *curl_multi_strerror(CURLMcode); + +/* + * Name: curl_multi_socket() and + * curl_multi_socket_all() + * + * Desc: An alternative version of curl_multi_perform() that allows the + * application to pass in one of the file descriptors that have been + * detected to have "action" on them and let libcurl perform. + * See man page for details. + */ +#define CURL_POLL_NONE 0 +#define CURL_POLL_IN 1 +#define CURL_POLL_OUT 2 +#define CURL_POLL_INOUT 3 +#define CURL_POLL_REMOVE 4 + +#define CURL_SOCKET_TIMEOUT CURL_SOCKET_BAD + +#define CURL_CSELECT_IN 0x01 +#define CURL_CSELECT_OUT 0x02 +#define CURL_CSELECT_ERR 0x04 + +typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */ + curl_socket_t s, /* socket */ + int what, /* see above */ + void *userp, /* private callback + pointer */ + void *socketp); /* private socket + pointer */ +/* + * Name: curl_multi_timer_callback + * + * Desc: Called by libcurl whenever the library detects a change in the + * maximum number of milliseconds the app is allowed to wait before + * curl_multi_socket() or curl_multi_perform() must be called + * (to allow libcurl's timed events to take place). + * + * Returns: The callback should return zero. + */ +typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */ + long timeout_ms, /* see above */ + void *userp); /* private callback + pointer */ + +CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, + int *running_handles); + +CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle, + curl_socket_t s, + int ev_bitmask, + int *running_handles); + +CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle, + int *running_handles); + +#ifndef CURL_ALLOW_OLD_MULTI_SOCKET +/* This macro below was added in 7.16.3 to push users who recompile to use + the new curl_multi_socket_action() instead of the old curl_multi_socket() +*/ +#define curl_multi_socket(x,y,z) curl_multi_socket_action(x,y,0,z) +#endif + +/* + * Name: curl_multi_timeout() + * + * Desc: Returns the maximum number of milliseconds the app is allowed to + * wait before curl_multi_socket() or curl_multi_perform() must be + * called (to allow libcurl's timed events to take place). + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle, + long *milliseconds); + +#undef CINIT /* re-using the same name as in curl.h */ + +#ifdef CURL_ISOCPP +#define CINIT(name,type,num) CURLMOPT_ ## name = CURLOPTTYPE_ ## type + num +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define LONG CURLOPTTYPE_LONG +#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT +#define OFF_T CURLOPTTYPE_OFF_T +#define CINIT(name,type,number) CURLMOPT_/**/name = type + number +#endif + +typedef enum { + /* This is the socket callback function pointer */ + CINIT(SOCKETFUNCTION, FUNCTIONPOINT, 1), + + /* This is the argument passed to the socket callback */ + CINIT(SOCKETDATA, OBJECTPOINT, 2), + + /* set to 1 to enable pipelining for this multi handle */ + CINIT(PIPELINING, LONG, 3), + + /* This is the timer callback function pointer */ + CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4), + + /* This is the argument passed to the timer callback */ + CINIT(TIMERDATA, OBJECTPOINT, 5), + + /* maximum number of entries in the connection cache */ + CINIT(MAXCONNECTS, LONG, 6), + + /* maximum number of (pipelining) connections to one host */ + CINIT(MAX_HOST_CONNECTIONS, LONG, 7), + + /* maximum number of requests in a pipeline */ + CINIT(MAX_PIPELINE_LENGTH, LONG, 8), + + /* a connection with a content-length longer than this + will not be considered for pipelining */ + CINIT(CONTENT_LENGTH_PENALTY_SIZE, OFF_T, 9), + + /* a connection with a chunk length longer than this + will not be considered for pipelining */ + CINIT(CHUNK_LENGTH_PENALTY_SIZE, OFF_T, 10), + + /* a list of site names(+port) that are blacklisted from + pipelining */ + CINIT(PIPELINING_SITE_BL, OBJECTPOINT, 11), + + /* a list of server types that are blacklisted from + pipelining */ + CINIT(PIPELINING_SERVER_BL, OBJECTPOINT, 12), + + /* maximum number of open connections in total */ + CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13), + + /* This is the server push callback function pointer */ + CINIT(PUSHFUNCTION, FUNCTIONPOINT, 14), + + /* This is the argument passed to the server push callback */ + CINIT(PUSHDATA, OBJECTPOINT, 15), + + CURLMOPT_LASTENTRY /* the last unused */ +} CURLMoption; + + +/* + * Name: curl_multi_setopt() + * + * Desc: Sets options for the multi handle. + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, + CURLMoption option, ...); + + +/* + * Name: curl_multi_assign() + * + * Desc: This function sets an association in the multi handle between the + * given socket and a private pointer of the application. This is + * (only) useful for curl_multi_socket uses. + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, + curl_socket_t sockfd, void *sockp); + + +/* + * Name: curl_push_callback + * + * Desc: This callback gets called when a new stream is being pushed by the + * server. It approves or denies the new stream. + * + * Returns: CURL_PUSH_OK or CURL_PUSH_DENY. + */ +#define CURL_PUSH_OK 0 +#define CURL_PUSH_DENY 1 + +struct curl_pushheaders; /* forward declaration only */ + +CURL_EXTERN char *curl_pushheader_bynum(struct curl_pushheaders *h, + size_t num); +CURL_EXTERN char *curl_pushheader_byname(struct curl_pushheaders *h, + const char *name); + +typedef int (*curl_push_callback)(CURL *parent, + CURL *easy, + size_t num_headers, + struct curl_pushheaders *headers, + void *userp); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif diff --git a/ext/curl-7.58.0/Win32/include/curl/stdcheaders.h b/ext/curl-7.58.0/Win32/include/curl/stdcheaders.h new file mode 100644 index 000000000..027b6f421 --- /dev/null +++ b/ext/curl-7.58.0/Win32/include/curl/stdcheaders.h @@ -0,0 +1,33 @@ +#ifndef __STDC_HEADERS_H +#define __STDC_HEADERS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include + +size_t fread(void *, size_t, size_t, FILE *); +size_t fwrite(const void *, size_t, size_t, FILE *); + +int strcasecmp(const char *, const char *); +int strncasecmp(const char *, const char *, size_t); + +#endif /* __STDC_HEADERS_H */ diff --git a/ext/curl-7.58.0/Win32/include/curl/system.h b/ext/curl-7.58.0/Win32/include/curl/system.h new file mode 100644 index 000000000..07bbd9ca9 --- /dev/null +++ b/ext/curl-7.58.0/Win32/include/curl/system.h @@ -0,0 +1,473 @@ +#ifndef __CURL_SYSTEM_H +#define __CURL_SYSTEM_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* + * Try to keep one section per platform, compiler and architecture, otherwise, + * if an existing section is reused for a different one and later on the + * original is adjusted, probably the piggybacking one can be adversely + * changed. + * + * In order to differentiate between platforms/compilers/architectures use + * only compiler built in predefined preprocessor symbols. + * + * curl_off_t + * ---------- + * + * For any given platform/compiler curl_off_t must be typedef'ed to a 64-bit + * wide signed integral data type. The width of this data type must remain + * constant and independent of any possible large file support settings. + * + * As an exception to the above, curl_off_t shall be typedef'ed to a 32-bit + * wide signed integral data type if there is no 64-bit type. + * + * As a general rule, curl_off_t shall not be mapped to off_t. This rule shall + * only be violated if off_t is the only 64-bit data type available and the + * size of off_t is independent of large file support settings. Keep your + * build on the safe side avoiding an off_t gating. If you have a 64-bit + * off_t then take for sure that another 64-bit data type exists, dig deeper + * and you will find it. + * + */ + +#if defined(__DJGPP__) || defined(__GO32__) +# if defined(__DJGPP__) && (__DJGPP__ > 1) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__SALFORDC__) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__BORLANDC__) +# if (__BORLANDC__ < 0x520) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__TURBOC__) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__WATCOMC__) +# if defined(__386__) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__POCC__) +# if (__POCC__ < 280) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# elif defined(_MSC_VER) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__LCC__) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__SYMBIAN32__) +# if defined(__EABI__) /* Treat all ARM compilers equally */ +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__CW32__) +# pragma longlong on +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__VC32__) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int + +#elif defined(__MWERKS__) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(_WIN32_WCE) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__MINGW32__) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_WS2TCPIP_H 1 + +#elif defined(__VMS) +# if defined(__VAX) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int + +#elif defined(__OS400__) +# if defined(__ILEC400__) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(__MVS__) +# if defined(__IBMC__) || defined(__IBMCPP__) +# if defined(_ILP32) +# elif defined(_LP64) +# endif +# if defined(_LONG_LONG) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(__370__) +# if defined(__IBMC__) || defined(__IBMCPP__) +# if defined(_ILP32) +# elif defined(_LP64) +# endif +# if defined(_LONG_LONG) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(TPF) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__TINYC__) /* also known as tcc */ + +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#elif defined(__SUNPRO_C) /* Oracle Solaris Studio */ +# if !defined(__LP64) && (defined(__ILP32) || \ + defined(__i386) || defined(__sparcv8)) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__LP64) || \ + defined(__amd64) || defined(__sparcv9) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +/* ===================================== */ +/* KEEP MSVC THE PENULTIMATE ENTRY */ +/* ===================================== */ + +#elif defined(_MSC_VER) +# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +/* ===================================== */ +/* KEEP GENERIC GCC THE LAST ENTRY */ +/* ===================================== */ + +#elif defined(__GNUC__) +# if !defined(__LP64__) && \ + (defined(__ILP32__) || defined(__i386__) || defined(__hppa__) || \ + defined(__ppc__) || defined(__powerpc__) || defined(__arm__) || \ + defined(__sparc__) || defined(__mips__) || defined(__sh__) || \ + defined(__XTENSA__) || \ + (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4) || \ + (defined(__LONG_MAX__) && __LONG_MAX__ == 2147483647L)) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__LP64__) || \ + defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) || \ + (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 8) || \ + (defined(__LONG_MAX__) && __LONG_MAX__ == 9223372036854775807L) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#else +/* generic "safe guess" on old 32 bit style */ +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +#endif + +#ifdef _AIX +/* AIX needs */ +#define CURL_PULL_SYS_POLL_H +#endif + + +/* CURL_PULL_WS2TCPIP_H is defined above when inclusion of header file */ +/* ws2tcpip.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_WS2TCPIP_H +# include +# include +# include +#endif + +/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file */ +/* sys/types.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_TYPES_H +# include +#endif + +/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file */ +/* sys/socket.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_SOCKET_H +# include +#endif + +/* CURL_PULL_SYS_POLL_H is defined above when inclusion of header file */ +/* sys/poll.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_POLL_H +# include +#endif + +/* Data type definition of curl_socklen_t. */ +#ifdef CURL_TYPEOF_CURL_SOCKLEN_T + typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t; +#endif + +/* Data type definition of curl_off_t. */ + +#ifdef CURL_TYPEOF_CURL_OFF_T + typedef CURL_TYPEOF_CURL_OFF_T curl_off_t; +#endif + +/* + * CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow + * these to be visible and exported by the external libcurl interface API, + * while also making them visible to the library internals, simply including + * curl_setup.h, without actually needing to include curl.h internally. + * If some day this section would grow big enough, all this should be moved + * to its own header file. + */ + +/* + * Figure out if we can use the ## preprocessor operator, which is supported + * by ISO/ANSI C and C++. Some compilers support it without setting __STDC__ + * or __cplusplus so we need to carefully check for them too. + */ + +#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \ + defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \ + defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \ + defined(__ILEC400__) + /* This compiler is believed to have an ISO compatible preprocessor */ +#define CURL_ISOCPP +#else + /* This compiler is believed NOT to have an ISO compatible preprocessor */ +#undef CURL_ISOCPP +#endif + +/* + * Macros for minimum-width signed and unsigned curl_off_t integer constants. + */ + +#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551) +# define __CURL_OFF_T_C_HLPR2(x) x +# define __CURL_OFF_T_C_HLPR1(x) __CURL_OFF_T_C_HLPR2(x) +# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \ + __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T) +# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \ + __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU) +#else +# ifdef CURL_ISOCPP +# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix +# else +# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix +# endif +# define __CURL_OFF_T_C_HLPR1(Val,Suffix) __CURL_OFF_T_C_HLPR2(Val,Suffix) +# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T) +# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU) +#endif + +#endif /* __CURL_SYSTEM_H */ diff --git a/ext/curl-7.58.0/Win32/include/curl/typecheck-gcc.h b/ext/curl-7.58.0/Win32/include/curl/typecheck-gcc.h new file mode 100644 index 000000000..10c74c764 --- /dev/null +++ b/ext/curl-7.58.0/Win32/include/curl/typecheck-gcc.h @@ -0,0 +1,683 @@ +#ifndef __CURL_TYPECHECK_GCC_H +#define __CURL_TYPECHECK_GCC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* wraps curl_easy_setopt() with typechecking */ + +/* To add a new kind of warning, add an + * if(_curl_is_sometype_option(_curl_opt)) + * if(!_curl_is_sometype(value)) + * _curl_easy_setopt_err_sometype(); + * block and define _curl_is_sometype_option, _curl_is_sometype and + * _curl_easy_setopt_err_sometype below + * + * NOTE: We use two nested 'if' statements here instead of the && operator, in + * order to work around gcc bug #32061. It affects only gcc 4.3.x/4.4.x + * when compiling with -Wlogical-op. + * + * To add an option that uses the same type as an existing option, you'll just + * need to extend the appropriate _curl_*_option macro + */ +#define curl_easy_setopt(handle, option, value) \ +__extension__ ({ \ + __typeof__(option) _curl_opt = option; \ + if(__builtin_constant_p(_curl_opt)) { \ + if(_curl_is_long_option(_curl_opt)) \ + if(!_curl_is_long(value)) \ + _curl_easy_setopt_err_long(); \ + if(_curl_is_off_t_option(_curl_opt)) \ + if(!_curl_is_off_t(value)) \ + _curl_easy_setopt_err_curl_off_t(); \ + if(_curl_is_string_option(_curl_opt)) \ + if(!_curl_is_string(value)) \ + _curl_easy_setopt_err_string(); \ + if(_curl_is_write_cb_option(_curl_opt)) \ + if(!_curl_is_write_cb(value)) \ + _curl_easy_setopt_err_write_callback(); \ + if((_curl_opt) == CURLOPT_READFUNCTION) \ + if(!_curl_is_read_cb(value)) \ + _curl_easy_setopt_err_read_cb(); \ + if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \ + if(!_curl_is_ioctl_cb(value)) \ + _curl_easy_setopt_err_ioctl_cb(); \ + if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \ + if(!_curl_is_sockopt_cb(value)) \ + _curl_easy_setopt_err_sockopt_cb(); \ + if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \ + if(!_curl_is_opensocket_cb(value)) \ + _curl_easy_setopt_err_opensocket_cb(); \ + if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \ + if(!_curl_is_progress_cb(value)) \ + _curl_easy_setopt_err_progress_cb(); \ + if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \ + if(!_curl_is_debug_cb(value)) \ + _curl_easy_setopt_err_debug_cb(); \ + if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \ + if(!_curl_is_ssl_ctx_cb(value)) \ + _curl_easy_setopt_err_ssl_ctx_cb(); \ + if(_curl_is_conv_cb_option(_curl_opt)) \ + if(!_curl_is_conv_cb(value)) \ + _curl_easy_setopt_err_conv_cb(); \ + if((_curl_opt) == CURLOPT_SEEKFUNCTION) \ + if(!_curl_is_seek_cb(value)) \ + _curl_easy_setopt_err_seek_cb(); \ + if(_curl_is_cb_data_option(_curl_opt)) \ + if(!_curl_is_cb_data(value)) \ + _curl_easy_setopt_err_cb_data(); \ + if((_curl_opt) == CURLOPT_ERRORBUFFER) \ + if(!_curl_is_error_buffer(value)) \ + _curl_easy_setopt_err_error_buffer(); \ + if((_curl_opt) == CURLOPT_STDERR) \ + if(!_curl_is_FILE(value)) \ + _curl_easy_setopt_err_FILE(); \ + if(_curl_is_postfields_option(_curl_opt)) \ + if(!_curl_is_postfields(value)) \ + _curl_easy_setopt_err_postfields(); \ + if((_curl_opt) == CURLOPT_HTTPPOST) \ + if(!_curl_is_arr((value), struct curl_httppost)) \ + _curl_easy_setopt_err_curl_httpost(); \ + if((_curl_opt) == CURLOPT_MIMEPOST) \ + if(!_curl_is_ptr((value), curl_mime)) \ + _curl_easy_setopt_err_curl_mimepost(); \ + if(_curl_is_slist_option(_curl_opt)) \ + if(!_curl_is_arr((value), struct curl_slist)) \ + _curl_easy_setopt_err_curl_slist(); \ + if((_curl_opt) == CURLOPT_SHARE) \ + if(!_curl_is_ptr((value), CURLSH)) \ + _curl_easy_setopt_err_CURLSH(); \ + } \ + curl_easy_setopt(handle, _curl_opt, value); \ +}) + +/* wraps curl_easy_getinfo() with typechecking */ +/* FIXME: don't allow const pointers */ +#define curl_easy_getinfo(handle, info, arg) \ +__extension__ ({ \ + __typeof__(info) _curl_info = info; \ + if(__builtin_constant_p(_curl_info)) { \ + if(_curl_is_string_info(_curl_info)) \ + if(!_curl_is_arr((arg), char *)) \ + _curl_easy_getinfo_err_string(); \ + if(_curl_is_long_info(_curl_info)) \ + if(!_curl_is_arr((arg), long)) \ + _curl_easy_getinfo_err_long(); \ + if(_curl_is_double_info(_curl_info)) \ + if(!_curl_is_arr((arg), double)) \ + _curl_easy_getinfo_err_double(); \ + if(_curl_is_slist_info(_curl_info)) \ + if(!_curl_is_arr((arg), struct curl_slist *)) \ + _curl_easy_getinfo_err_curl_slist(); \ + if(_curl_is_tlssessioninfo_info(_curl_info)) \ + if(!_curl_is_arr((arg), struct curl_tlssessioninfo *)) \ + _curl_easy_getinfo_err_curl_tlssesssioninfo(); \ + if(_curl_is_certinfo_info(_curl_info)) \ + if(!_curl_is_arr((arg), struct curl_certinfo *)) \ + _curl_easy_getinfo_err_curl_certinfo(); \ + if(_curl_is_socket_info(_curl_info)) \ + if(!_curl_is_arr((arg), curl_socket_t)) \ + _curl_easy_getinfo_err_curl_socket(); \ + if(_curl_is_off_t_info(_curl_info)) \ + if(!_curl_is_arr((arg), curl_off_t)) \ + _curl_easy_getinfo_err_curl_off_t(); \ + } \ + curl_easy_getinfo(handle, _curl_info, arg); \ +}) + +/* TODO: typechecking for curl_share_setopt() and curl_multi_setopt(), + * for now just make sure that the functions are called with three + * arguments + */ +#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) +#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) + + +/* the actual warnings, triggered by calling the _curl_easy_setopt_err* + * functions */ + +/* To define a new warning, use _CURL_WARNING(identifier, "message") */ +#define _CURL_WARNING(id, message) \ + static void __attribute__((__warning__(message))) \ + __attribute__((__unused__)) __attribute__((__noinline__)) \ + id(void) { __asm__(""); } + +_CURL_WARNING(_curl_easy_setopt_err_long, + "curl_easy_setopt expects a long argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_off_t, + "curl_easy_setopt expects a curl_off_t argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_string, + "curl_easy_setopt expects a " + "string ('char *' or char[]) argument for this option" + ) +_CURL_WARNING(_curl_easy_setopt_err_write_callback, + "curl_easy_setopt expects a curl_write_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_read_cb, + "curl_easy_setopt expects a curl_read_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_ioctl_cb, + "curl_easy_setopt expects a curl_ioctl_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_sockopt_cb, + "curl_easy_setopt expects a curl_sockopt_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_opensocket_cb, + "curl_easy_setopt expects a " + "curl_opensocket_callback argument for this option" + ) +_CURL_WARNING(_curl_easy_setopt_err_progress_cb, + "curl_easy_setopt expects a curl_progress_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_debug_cb, + "curl_easy_setopt expects a curl_debug_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_ssl_ctx_cb, + "curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_conv_cb, + "curl_easy_setopt expects a curl_conv_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_seek_cb, + "curl_easy_setopt expects a curl_seek_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_cb_data, + "curl_easy_setopt expects a " + "private data pointer as argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_error_buffer, + "curl_easy_setopt expects a " + "char buffer of CURL_ERROR_SIZE as argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_FILE, + "curl_easy_setopt expects a 'FILE *' argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_postfields, + "curl_easy_setopt expects a 'void *' or 'char *' argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_httpost, + "curl_easy_setopt expects a 'struct curl_httppost *' " + "argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_mimepost, + "curl_easy_setopt expects a 'curl_mime *' " + "argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_slist, + "curl_easy_setopt expects a 'struct curl_slist *' argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_CURLSH, + "curl_easy_setopt expects a CURLSH* argument for this option") + +_CURL_WARNING(_curl_easy_getinfo_err_string, + "curl_easy_getinfo expects a pointer to 'char *' for this info") +_CURL_WARNING(_curl_easy_getinfo_err_long, + "curl_easy_getinfo expects a pointer to long for this info") +_CURL_WARNING(_curl_easy_getinfo_err_double, + "curl_easy_getinfo expects a pointer to double for this info") +_CURL_WARNING(_curl_easy_getinfo_err_curl_slist, + "curl_easy_getinfo expects a pointer to 'struct curl_slist *' for this info") +_CURL_WARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo, + "curl_easy_getinfo expects a pointer to " + "'struct curl_tlssessioninfo *' for this info") +_CURL_WARNING(_curl_easy_getinfo_err_curl_certinfo, + "curl_easy_getinfo expects a pointer to " + "'struct curl_certinfo *' for this info") +_CURL_WARNING(_curl_easy_getinfo_err_curl_socket, + "curl_easy_getinfo expects a pointer to curl_socket_t for this info") +_CURL_WARNING(_curl_easy_getinfo_err_curl_off_t, + "curl_easy_getinfo expects a pointer to curl_off_t for this info") + +/* groups of curl_easy_setops options that take the same type of argument */ + +/* To add a new option to one of the groups, just add + * (option) == CURLOPT_SOMETHING + * to the or-expression. If the option takes a long or curl_off_t, you don't + * have to do anything + */ + +/* evaluates to true if option takes a long argument */ +#define _curl_is_long_option(option) \ + (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT) + +#define _curl_is_off_t_option(option) \ + ((option) > CURLOPTTYPE_OFF_T) + +/* evaluates to true if option takes a char* argument */ +#define _curl_is_string_option(option) \ + ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET || \ + (option) == CURLOPT_ACCEPT_ENCODING || \ + (option) == CURLOPT_CAINFO || \ + (option) == CURLOPT_CAPATH || \ + (option) == CURLOPT_COOKIE || \ + (option) == CURLOPT_COOKIEFILE || \ + (option) == CURLOPT_COOKIEJAR || \ + (option) == CURLOPT_COOKIELIST || \ + (option) == CURLOPT_CRLFILE || \ + (option) == CURLOPT_CUSTOMREQUEST || \ + (option) == CURLOPT_DEFAULT_PROTOCOL || \ + (option) == CURLOPT_DNS_INTERFACE || \ + (option) == CURLOPT_DNS_LOCAL_IP4 || \ + (option) == CURLOPT_DNS_LOCAL_IP6 || \ + (option) == CURLOPT_DNS_SERVERS || \ + (option) == CURLOPT_EGDSOCKET || \ + (option) == CURLOPT_FTPPORT || \ + (option) == CURLOPT_FTP_ACCOUNT || \ + (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ + (option) == CURLOPT_INTERFACE || \ + (option) == CURLOPT_ISSUERCERT || \ + (option) == CURLOPT_KEYPASSWD || \ + (option) == CURLOPT_KRBLEVEL || \ + (option) == CURLOPT_LOGIN_OPTIONS || \ + (option) == CURLOPT_MAIL_AUTH || \ + (option) == CURLOPT_MAIL_FROM || \ + (option) == CURLOPT_NETRC_FILE || \ + (option) == CURLOPT_NOPROXY || \ + (option) == CURLOPT_PASSWORD || \ + (option) == CURLOPT_PINNEDPUBLICKEY || \ + (option) == CURLOPT_PRE_PROXY || \ + (option) == CURLOPT_PROXY || \ + (option) == CURLOPT_PROXYPASSWORD || \ + (option) == CURLOPT_PROXYUSERNAME || \ + (option) == CURLOPT_PROXYUSERPWD || \ + (option) == CURLOPT_PROXY_CAINFO || \ + (option) == CURLOPT_PROXY_CAPATH || \ + (option) == CURLOPT_PROXY_CRLFILE || \ + (option) == CURLOPT_PROXY_KEYPASSWD || \ + (option) == CURLOPT_PROXY_PINNEDPUBLICKEY || \ + (option) == CURLOPT_PROXY_SERVICE_NAME || \ + (option) == CURLOPT_PROXY_SSLCERT || \ + (option) == CURLOPT_PROXY_SSLCERTTYPE || \ + (option) == CURLOPT_PROXY_SSLKEY || \ + (option) == CURLOPT_PROXY_SSLKEYTYPE || \ + (option) == CURLOPT_PROXY_SSL_CIPHER_LIST || \ + (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD || \ + (option) == CURLOPT_PROXY_TLSAUTH_USERNAME || \ + (option) == CURLOPT_PROXY_TLSAUTH_TYPE || \ + (option) == CURLOPT_RANDOM_FILE || \ + (option) == CURLOPT_RANGE || \ + (option) == CURLOPT_REFERER || \ + (option) == CURLOPT_RTSP_SESSION_ID || \ + (option) == CURLOPT_RTSP_STREAM_URI || \ + (option) == CURLOPT_RTSP_TRANSPORT || \ + (option) == CURLOPT_SERVICE_NAME || \ + (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \ + (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \ + (option) == CURLOPT_SSH_KNOWNHOSTS || \ + (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \ + (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \ + (option) == CURLOPT_SSLCERT || \ + (option) == CURLOPT_SSLCERTTYPE || \ + (option) == CURLOPT_SSLENGINE || \ + (option) == CURLOPT_SSLKEY || \ + (option) == CURLOPT_SSLKEYTYPE || \ + (option) == CURLOPT_SSL_CIPHER_LIST || \ + (option) == CURLOPT_TLSAUTH_PASSWORD || \ + (option) == CURLOPT_TLSAUTH_TYPE || \ + (option) == CURLOPT_TLSAUTH_USERNAME || \ + (option) == CURLOPT_UNIX_SOCKET_PATH || \ + (option) == CURLOPT_URL || \ + (option) == CURLOPT_USERAGENT || \ + (option) == CURLOPT_USERNAME || \ + (option) == CURLOPT_USERPWD || \ + (option) == CURLOPT_XOAUTH2_BEARER || \ + 0) + +/* evaluates to true if option takes a curl_write_callback argument */ +#define _curl_is_write_cb_option(option) \ + ((option) == CURLOPT_HEADERFUNCTION || \ + (option) == CURLOPT_WRITEFUNCTION) + +/* evaluates to true if option takes a curl_conv_callback argument */ +#define _curl_is_conv_cb_option(option) \ + ((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION || \ + (option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION || \ + (option) == CURLOPT_CONV_FROM_UTF8_FUNCTION) + +/* evaluates to true if option takes a data argument to pass to a callback */ +#define _curl_is_cb_data_option(option) \ + ((option) == CURLOPT_CHUNK_DATA || \ + (option) == CURLOPT_CLOSESOCKETDATA || \ + (option) == CURLOPT_DEBUGDATA || \ + (option) == CURLOPT_FNMATCH_DATA || \ + (option) == CURLOPT_HEADERDATA || \ + (option) == CURLOPT_INTERLEAVEDATA || \ + (option) == CURLOPT_IOCTLDATA || \ + (option) == CURLOPT_OPENSOCKETDATA || \ + (option) == CURLOPT_PRIVATE || \ + (option) == CURLOPT_PROGRESSDATA || \ + (option) == CURLOPT_READDATA || \ + (option) == CURLOPT_SEEKDATA || \ + (option) == CURLOPT_SOCKOPTDATA || \ + (option) == CURLOPT_SSH_KEYDATA || \ + (option) == CURLOPT_SSL_CTX_DATA || \ + (option) == CURLOPT_WRITEDATA || \ + 0) + +/* evaluates to true if option takes a POST data argument (void* or char*) */ +#define _curl_is_postfields_option(option) \ + ((option) == CURLOPT_POSTFIELDS || \ + (option) == CURLOPT_COPYPOSTFIELDS || \ + 0) + +/* evaluates to true if option takes a struct curl_slist * argument */ +#define _curl_is_slist_option(option) \ + ((option) == CURLOPT_HTTP200ALIASES || \ + (option) == CURLOPT_HTTPHEADER || \ + (option) == CURLOPT_MAIL_RCPT || \ + (option) == CURLOPT_POSTQUOTE || \ + (option) == CURLOPT_PREQUOTE || \ + (option) == CURLOPT_PROXYHEADER || \ + (option) == CURLOPT_QUOTE || \ + (option) == CURLOPT_RESOLVE || \ + (option) == CURLOPT_TELNETOPTIONS || \ + 0) + +/* groups of curl_easy_getinfo infos that take the same type of argument */ + +/* evaluates to true if info expects a pointer to char * argument */ +#define _curl_is_string_info(info) \ + (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG) + +/* evaluates to true if info expects a pointer to long argument */ +#define _curl_is_long_info(info) \ + (CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE) + +/* evaluates to true if info expects a pointer to double argument */ +#define _curl_is_double_info(info) \ + (CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST) + +/* true if info expects a pointer to struct curl_slist * argument */ +#define _curl_is_slist_info(info) \ + (((info) == CURLINFO_SSL_ENGINES) || ((info) == CURLINFO_COOKIELIST)) + +/* true if info expects a pointer to struct curl_tlssessioninfo * argument */ +#define _curl_is_tlssessioninfo_info(info) \ + (((info) == CURLINFO_TLS_SSL_PTR) || ((info) == CURLINFO_TLS_SESSION)) + +/* true if info expects a pointer to struct curl_certinfo * argument */ +#define _curl_is_certinfo_info(info) ((info) == CURLINFO_CERTINFO) + +/* true if info expects a pointer to struct curl_socket_t argument */ +#define _curl_is_socket_info(info) \ + (CURLINFO_SOCKET < (info) && (info) < CURLINFO_OFF_T) + +/* true if info expects a pointer to curl_off_t argument */ +#define _curl_is_off_t_info(info) \ + (CURLINFO_OFF_T < (info)) + + +/* typecheck helpers -- check whether given expression has requested type*/ + +/* For pointers, you can use the _curl_is_ptr/_curl_is_arr macros, + * otherwise define a new macro. Search for __builtin_types_compatible_p + * in the GCC manual. + * NOTE: these macros MUST NOT EVALUATE their arguments! The argument is + * the actual expression passed to the curl_easy_setopt macro. This + * means that you can only apply the sizeof and __typeof__ operators, no + * == or whatsoever. + */ + +/* XXX: should evaluate to true iff expr is a pointer */ +#define _curl_is_any_ptr(expr) \ + (sizeof(expr) == sizeof(void *)) + +/* evaluates to true if expr is NULL */ +/* XXX: must not evaluate expr, so this check is not accurate */ +#define _curl_is_NULL(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), __typeof__(NULL))) + +/* evaluates to true if expr is type*, const type* or NULL */ +#define _curl_is_ptr(expr, type) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), type *) || \ + __builtin_types_compatible_p(__typeof__(expr), const type *)) + +/* evaluates to true if expr is one of type[], type*, NULL or const type* */ +#define _curl_is_arr(expr, type) \ + (_curl_is_ptr((expr), type) || \ + __builtin_types_compatible_p(__typeof__(expr), type [])) + +/* evaluates to true if expr is a string */ +#define _curl_is_string(expr) \ + (_curl_is_arr((expr), char) || \ + _curl_is_arr((expr), signed char) || \ + _curl_is_arr((expr), unsigned char)) + +/* evaluates to true if expr is a long (no matter the signedness) + * XXX: for now, int is also accepted (and therefore short and char, which + * are promoted to int when passed to a variadic function) */ +#define _curl_is_long(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), long) || \ + __builtin_types_compatible_p(__typeof__(expr), signed long) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned long) || \ + __builtin_types_compatible_p(__typeof__(expr), int) || \ + __builtin_types_compatible_p(__typeof__(expr), signed int) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned int) || \ + __builtin_types_compatible_p(__typeof__(expr), short) || \ + __builtin_types_compatible_p(__typeof__(expr), signed short) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned short) || \ + __builtin_types_compatible_p(__typeof__(expr), char) || \ + __builtin_types_compatible_p(__typeof__(expr), signed char) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned char)) + +/* evaluates to true if expr is of type curl_off_t */ +#define _curl_is_off_t(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), curl_off_t)) + +/* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */ +/* XXX: also check size of an char[] array? */ +#define _curl_is_error_buffer(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), char *) || \ + __builtin_types_compatible_p(__typeof__(expr), char[])) + +/* evaluates to true if expr is of type (const) void* or (const) FILE* */ +#if 0 +#define _curl_is_cb_data(expr) \ + (_curl_is_ptr((expr), void) || \ + _curl_is_ptr((expr), FILE)) +#else /* be less strict */ +#define _curl_is_cb_data(expr) \ + _curl_is_any_ptr(expr) +#endif + +/* evaluates to true if expr is of type FILE* */ +#define _curl_is_FILE(expr) \ + (_curl_is_NULL(expr) || \ + (__builtin_types_compatible_p(__typeof__(expr), FILE *))) + +/* evaluates to true if expr can be passed as POST data (void* or char*) */ +#define _curl_is_postfields(expr) \ + (_curl_is_ptr((expr), void) || \ + _curl_is_arr((expr), char)) + +/* FIXME: the whole callback checking is messy... + * The idea is to tolerate char vs. void and const vs. not const + * pointers in arguments at least + */ +/* helper: __builtin_types_compatible_p distinguishes between functions and + * function pointers, hide it */ +#define _curl_callback_compatible(func, type) \ + (__builtin_types_compatible_p(__typeof__(func), type) || \ + __builtin_types_compatible_p(__typeof__(func) *, type)) + +/* evaluates to true if expr is of type curl_read_callback or "similar" */ +#define _curl_is_read_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), __typeof__(fread) *) || \ + _curl_callback_compatible((expr), curl_read_callback) || \ + _curl_callback_compatible((expr), _curl_read_callback1) || \ + _curl_callback_compatible((expr), _curl_read_callback2) || \ + _curl_callback_compatible((expr), _curl_read_callback3) || \ + _curl_callback_compatible((expr), _curl_read_callback4) || \ + _curl_callback_compatible((expr), _curl_read_callback5) || \ + _curl_callback_compatible((expr), _curl_read_callback6)) +typedef size_t (*_curl_read_callback1)(char *, size_t, size_t, void *); +typedef size_t (*_curl_read_callback2)(char *, size_t, size_t, const void *); +typedef size_t (*_curl_read_callback3)(char *, size_t, size_t, FILE *); +typedef size_t (*_curl_read_callback4)(void *, size_t, size_t, void *); +typedef size_t (*_curl_read_callback5)(void *, size_t, size_t, const void *); +typedef size_t (*_curl_read_callback6)(void *, size_t, size_t, FILE *); + +/* evaluates to true if expr is of type curl_write_callback or "similar" */ +#define _curl_is_write_cb(expr) \ + (_curl_is_read_cb(expr) || \ + _curl_callback_compatible((expr), __typeof__(fwrite) *) || \ + _curl_callback_compatible((expr), curl_write_callback) || \ + _curl_callback_compatible((expr), _curl_write_callback1) || \ + _curl_callback_compatible((expr), _curl_write_callback2) || \ + _curl_callback_compatible((expr), _curl_write_callback3) || \ + _curl_callback_compatible((expr), _curl_write_callback4) || \ + _curl_callback_compatible((expr), _curl_write_callback5) || \ + _curl_callback_compatible((expr), _curl_write_callback6)) +typedef size_t (*_curl_write_callback1)(const char *, size_t, size_t, void *); +typedef size_t (*_curl_write_callback2)(const char *, size_t, size_t, + const void *); +typedef size_t (*_curl_write_callback3)(const char *, size_t, size_t, FILE *); +typedef size_t (*_curl_write_callback4)(const void *, size_t, size_t, void *); +typedef size_t (*_curl_write_callback5)(const void *, size_t, size_t, + const void *); +typedef size_t (*_curl_write_callback6)(const void *, size_t, size_t, FILE *); + +/* evaluates to true if expr is of type curl_ioctl_callback or "similar" */ +#define _curl_is_ioctl_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_ioctl_callback) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback1) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback2) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback3) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback4)) +typedef curlioerr (*_curl_ioctl_callback1)(CURL *, int, void *); +typedef curlioerr (*_curl_ioctl_callback2)(CURL *, int, const void *); +typedef curlioerr (*_curl_ioctl_callback3)(CURL *, curliocmd, void *); +typedef curlioerr (*_curl_ioctl_callback4)(CURL *, curliocmd, const void *); + +/* evaluates to true if expr is of type curl_sockopt_callback or "similar" */ +#define _curl_is_sockopt_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_sockopt_callback) || \ + _curl_callback_compatible((expr), _curl_sockopt_callback1) || \ + _curl_callback_compatible((expr), _curl_sockopt_callback2)) +typedef int (*_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype); +typedef int (*_curl_sockopt_callback2)(const void *, curl_socket_t, + curlsocktype); + +/* evaluates to true if expr is of type curl_opensocket_callback or + "similar" */ +#define _curl_is_opensocket_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_opensocket_callback) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback1) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback2) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback3) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback4)) +typedef curl_socket_t (*_curl_opensocket_callback1) + (void *, curlsocktype, struct curl_sockaddr *); +typedef curl_socket_t (*_curl_opensocket_callback2) + (void *, curlsocktype, const struct curl_sockaddr *); +typedef curl_socket_t (*_curl_opensocket_callback3) + (const void *, curlsocktype, struct curl_sockaddr *); +typedef curl_socket_t (*_curl_opensocket_callback4) + (const void *, curlsocktype, const struct curl_sockaddr *); + +/* evaluates to true if expr is of type curl_progress_callback or "similar" */ +#define _curl_is_progress_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_progress_callback) || \ + _curl_callback_compatible((expr), _curl_progress_callback1) || \ + _curl_callback_compatible((expr), _curl_progress_callback2)) +typedef int (*_curl_progress_callback1)(void *, + double, double, double, double); +typedef int (*_curl_progress_callback2)(const void *, + double, double, double, double); + +/* evaluates to true if expr is of type curl_debug_callback or "similar" */ +#define _curl_is_debug_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_debug_callback) || \ + _curl_callback_compatible((expr), _curl_debug_callback1) || \ + _curl_callback_compatible((expr), _curl_debug_callback2) || \ + _curl_callback_compatible((expr), _curl_debug_callback3) || \ + _curl_callback_compatible((expr), _curl_debug_callback4) || \ + _curl_callback_compatible((expr), _curl_debug_callback5) || \ + _curl_callback_compatible((expr), _curl_debug_callback6) || \ + _curl_callback_compatible((expr), _curl_debug_callback7) || \ + _curl_callback_compatible((expr), _curl_debug_callback8)) +typedef int (*_curl_debug_callback1) (CURL *, + curl_infotype, char *, size_t, void *); +typedef int (*_curl_debug_callback2) (CURL *, + curl_infotype, char *, size_t, const void *); +typedef int (*_curl_debug_callback3) (CURL *, + curl_infotype, const char *, size_t, void *); +typedef int (*_curl_debug_callback4) (CURL *, + curl_infotype, const char *, size_t, const void *); +typedef int (*_curl_debug_callback5) (CURL *, + curl_infotype, unsigned char *, size_t, void *); +typedef int (*_curl_debug_callback6) (CURL *, + curl_infotype, unsigned char *, size_t, const void *); +typedef int (*_curl_debug_callback7) (CURL *, + curl_infotype, const unsigned char *, size_t, void *); +typedef int (*_curl_debug_callback8) (CURL *, + curl_infotype, const unsigned char *, size_t, const void *); + +/* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */ +/* this is getting even messier... */ +#define _curl_is_ssl_ctx_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_ssl_ctx_callback) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback1) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback2) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback3) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback4) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback5) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback6) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback7) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback8)) +typedef CURLcode (*_curl_ssl_ctx_callback1)(CURL *, void *, void *); +typedef CURLcode (*_curl_ssl_ctx_callback2)(CURL *, void *, const void *); +typedef CURLcode (*_curl_ssl_ctx_callback3)(CURL *, const void *, void *); +typedef CURLcode (*_curl_ssl_ctx_callback4)(CURL *, const void *, + const void *); +#ifdef HEADER_SSL_H +/* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX + * this will of course break if we're included before OpenSSL headers... + */ +typedef CURLcode (*_curl_ssl_ctx_callback5)(CURL *, SSL_CTX, void *); +typedef CURLcode (*_curl_ssl_ctx_callback6)(CURL *, SSL_CTX, const void *); +typedef CURLcode (*_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX, void *); +typedef CURLcode (*_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX, + const void *); +#else +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback5; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback6; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback7; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8; +#endif + +/* evaluates to true if expr is of type curl_conv_callback or "similar" */ +#define _curl_is_conv_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_conv_callback) || \ + _curl_callback_compatible((expr), _curl_conv_callback1) || \ + _curl_callback_compatible((expr), _curl_conv_callback2) || \ + _curl_callback_compatible((expr), _curl_conv_callback3) || \ + _curl_callback_compatible((expr), _curl_conv_callback4)) +typedef CURLcode (*_curl_conv_callback1)(char *, size_t length); +typedef CURLcode (*_curl_conv_callback2)(const char *, size_t length); +typedef CURLcode (*_curl_conv_callback3)(void *, size_t length); +typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length); + +/* evaluates to true if expr is of type curl_seek_callback or "similar" */ +#define _curl_is_seek_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_seek_callback) || \ + _curl_callback_compatible((expr), _curl_seek_callback1) || \ + _curl_callback_compatible((expr), _curl_seek_callback2)) +typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int); +typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int); + + +#endif /* __CURL_TYPECHECK_GCC_H */ diff --git a/ext/curl-7.58.0/Win32/lib/libcurl_a.lib b/ext/curl-7.58.0/Win32/lib/libcurl_a.lib new file mode 100644 index 000000000..241a0e8c7 Binary files /dev/null and b/ext/curl-7.58.0/Win32/lib/libcurl_a.lib differ diff --git a/ext/curl-7.58.0/Win32/lib/libcurl_a_debug.lib b/ext/curl-7.58.0/Win32/lib/libcurl_a_debug.lib new file mode 100644 index 000000000..ee9efe502 Binary files /dev/null and b/ext/curl-7.58.0/Win32/lib/libcurl_a_debug.lib differ diff --git a/ext/curl-7.58.0/x64/include/curl/curl.h b/ext/curl-7.58.0/x64/include/curl/curl.h new file mode 100644 index 000000000..7680acd18 --- /dev/null +++ b/ext/curl-7.58.0/x64/include/curl/curl.h @@ -0,0 +1,2751 @@ +#ifndef __CURL_CURL_H +#define __CURL_CURL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* + * If you have libcurl problems, all docs and details are found here: + * https://curl.haxx.se/libcurl/ + * + * curl-library mailing list subscription and unsubscription web interface: + * https://cool.haxx.se/mailman/listinfo/curl-library/ + */ + +#ifdef CURL_NO_OLDIES +#define CURL_STRICTER +#endif + +#include "curlver.h" /* libcurl version defines */ +#include "system.h" /* determine things run-time */ + +/* + * Define WIN32 when build target is Win32 API + */ + +#if (defined(_WIN32) || defined(__WIN32__)) && \ + !defined(WIN32) && !defined(__SYMBIAN32__) +#define WIN32 +#endif + +#include +#include + +#if defined(__FreeBSD__) && (__FreeBSD__ >= 2) +/* Needed for __FreeBSD_version symbol definition */ +#include +#endif + +/* The include stuff here below is mainly for time_t! */ +#include +#include + +#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__) +#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || \ + defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H)) +/* The check above prevents the winsock2 inclusion if winsock.h already was + included, since they can't co-exist without problems */ +#include +#include +#endif +#endif + +/* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish + libc5-based Linux systems. Only include it on systems that are known to + require it! */ +#if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \ + defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \ + defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \ + defined(__CYGWIN__) || \ + (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) +#include +#endif + +#if !defined(WIN32) && !defined(_WIN32_WCE) +#include +#endif + +#if !defined(WIN32) && !defined(__WATCOMC__) && !defined(__VXWORKS__) +#include +#endif + +#ifdef __BEOS__ +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER) +typedef struct Curl_easy CURL; +typedef struct Curl_share CURLSH; +#else +typedef void CURL; +typedef void CURLSH; +#endif + +/* + * libcurl external API function linkage decorations. + */ + +#ifdef CURL_STATICLIB +# define CURL_EXTERN +#elif defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__) +# if defined(BUILDING_LIBCURL) +# define CURL_EXTERN __declspec(dllexport) +# else +# define CURL_EXTERN __declspec(dllimport) +# endif +#elif defined(BUILDING_LIBCURL) && defined(CURL_HIDDEN_SYMBOLS) +# define CURL_EXTERN CURL_EXTERN_SYMBOL +#else +# define CURL_EXTERN +#endif + +#ifndef curl_socket_typedef +/* socket typedef */ +#if defined(WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H) +typedef SOCKET curl_socket_t; +#define CURL_SOCKET_BAD INVALID_SOCKET +#else +typedef int curl_socket_t; +#define CURL_SOCKET_BAD -1 +#endif +#define curl_socket_typedef +#endif /* curl_socket_typedef */ + +/* enum for the different supported SSL backends */ +typedef enum { + CURLSSLBACKEND_NONE = 0, + CURLSSLBACKEND_OPENSSL = 1, + CURLSSLBACKEND_GNUTLS = 2, + CURLSSLBACKEND_NSS = 3, + CURLSSLBACKEND_OBSOLETE4 = 4, /* Was QSOSSL. */ + CURLSSLBACKEND_GSKIT = 5, + CURLSSLBACKEND_POLARSSL = 6, + CURLSSLBACKEND_WOLFSSL = 7, + CURLSSLBACKEND_SCHANNEL = 8, + CURLSSLBACKEND_DARWINSSL = 9, + CURLSSLBACKEND_AXTLS = 10, + CURLSSLBACKEND_MBEDTLS = 11 +} curl_sslbackend; + +/* aliases for library clones and renames */ +#define CURLSSLBACKEND_LIBRESSL CURLSSLBACKEND_OPENSSL +#define CURLSSLBACKEND_BORINGSSL CURLSSLBACKEND_OPENSSL +#define CURLSSLBACKEND_CYASSL CURLSSLBACKEND_WOLFSSL + +struct curl_httppost { + struct curl_httppost *next; /* next entry in the list */ + char *name; /* pointer to allocated name */ + long namelength; /* length of name length */ + char *contents; /* pointer to allocated data contents */ + long contentslength; /* length of contents field, see also + CURL_HTTPPOST_LARGE */ + char *buffer; /* pointer to allocated buffer contents */ + long bufferlength; /* length of buffer field */ + char *contenttype; /* Content-Type */ + struct curl_slist *contentheader; /* list of extra headers for this form */ + struct curl_httppost *more; /* if one field name has more than one + file, this link should link to following + files */ + long flags; /* as defined below */ + +/* specified content is a file name */ +#define CURL_HTTPPOST_FILENAME (1<<0) +/* specified content is a file name */ +#define CURL_HTTPPOST_READFILE (1<<1) +/* name is only stored pointer do not free in formfree */ +#define CURL_HTTPPOST_PTRNAME (1<<2) +/* contents is only stored pointer do not free in formfree */ +#define CURL_HTTPPOST_PTRCONTENTS (1<<3) +/* upload file from buffer */ +#define CURL_HTTPPOST_BUFFER (1<<4) +/* upload file from pointer contents */ +#define CURL_HTTPPOST_PTRBUFFER (1<<5) +/* upload file contents by using the regular read callback to get the data and + pass the given pointer as custom pointer */ +#define CURL_HTTPPOST_CALLBACK (1<<6) +/* use size in 'contentlen', added in 7.46.0 */ +#define CURL_HTTPPOST_LARGE (1<<7) + + char *showfilename; /* The file name to show. If not set, the + actual file name will be used (if this + is a file part) */ + void *userp; /* custom pointer used for + HTTPPOST_CALLBACK posts */ + curl_off_t contentlen; /* alternative length of contents + field. Used if CURL_HTTPPOST_LARGE is + set. Added in 7.46.0 */ +}; + +/* This is the CURLOPT_PROGRESSFUNCTION callback proto. It is now considered + deprecated but was the only choice up until 7.31.0 */ +typedef int (*curl_progress_callback)(void *clientp, + double dltotal, + double dlnow, + double ultotal, + double ulnow); + +/* This is the CURLOPT_XFERINFOFUNCTION callback proto. It was introduced in + 7.32.0, it avoids floating point and provides more detailed information. */ +typedef int (*curl_xferinfo_callback)(void *clientp, + curl_off_t dltotal, + curl_off_t dlnow, + curl_off_t ultotal, + curl_off_t ulnow); + +#ifndef CURL_MAX_READ_SIZE + /* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */ +#define CURL_MAX_READ_SIZE 524288 +#endif + +#ifndef CURL_MAX_WRITE_SIZE + /* Tests have proven that 20K is a very bad buffer size for uploads on + Windows, while 16K for some odd reason performed a lot better. + We do the ifndef check to allow this value to easier be changed at build + time for those who feel adventurous. The practical minimum is about + 400 bytes since libcurl uses a buffer of this size as a scratch area + (unrelated to network send operations). */ +#define CURL_MAX_WRITE_SIZE 16384 +#endif + +#ifndef CURL_MAX_HTTP_HEADER +/* The only reason to have a max limit for this is to avoid the risk of a bad + server feeding libcurl with a never-ending header that will cause reallocs + infinitely */ +#define CURL_MAX_HTTP_HEADER (100*1024) +#endif + +/* This is a magic return code for the write callback that, when returned, + will signal libcurl to pause receiving on the current transfer. */ +#define CURL_WRITEFUNC_PAUSE 0x10000001 + +typedef size_t (*curl_write_callback)(char *buffer, + size_t size, + size_t nitems, + void *outstream); + + + +/* enumeration of file types */ +typedef enum { + CURLFILETYPE_FILE = 0, + CURLFILETYPE_DIRECTORY, + CURLFILETYPE_SYMLINK, + CURLFILETYPE_DEVICE_BLOCK, + CURLFILETYPE_DEVICE_CHAR, + CURLFILETYPE_NAMEDPIPE, + CURLFILETYPE_SOCKET, + CURLFILETYPE_DOOR, /* is possible only on Sun Solaris now */ + + CURLFILETYPE_UNKNOWN /* should never occur */ +} curlfiletype; + +#define CURLFINFOFLAG_KNOWN_FILENAME (1<<0) +#define CURLFINFOFLAG_KNOWN_FILETYPE (1<<1) +#define CURLFINFOFLAG_KNOWN_TIME (1<<2) +#define CURLFINFOFLAG_KNOWN_PERM (1<<3) +#define CURLFINFOFLAG_KNOWN_UID (1<<4) +#define CURLFINFOFLAG_KNOWN_GID (1<<5) +#define CURLFINFOFLAG_KNOWN_SIZE (1<<6) +#define CURLFINFOFLAG_KNOWN_HLINKCOUNT (1<<7) + +/* Content of this structure depends on information which is known and is + achievable (e.g. by FTP LIST parsing). Please see the url_easy_setopt(3) man + page for callbacks returning this structure -- some fields are mandatory, + some others are optional. The FLAG field has special meaning. */ +struct curl_fileinfo { + char *filename; + curlfiletype filetype; + time_t time; + unsigned int perm; + int uid; + int gid; + curl_off_t size; + long int hardlinks; + + struct { + /* If some of these fields is not NULL, it is a pointer to b_data. */ + char *time; + char *perm; + char *user; + char *group; + char *target; /* pointer to the target filename of a symlink */ + } strings; + + unsigned int flags; + + /* used internally */ + char *b_data; + size_t b_size; + size_t b_used; +}; + +/* return codes for CURLOPT_CHUNK_BGN_FUNCTION */ +#define CURL_CHUNK_BGN_FUNC_OK 0 +#define CURL_CHUNK_BGN_FUNC_FAIL 1 /* tell the lib to end the task */ +#define CURL_CHUNK_BGN_FUNC_SKIP 2 /* skip this chunk over */ + +/* if splitting of data transfer is enabled, this callback is called before + download of an individual chunk started. Note that parameter "remains" works + only for FTP wildcard downloading (for now), otherwise is not used */ +typedef long (*curl_chunk_bgn_callback)(const void *transfer_info, + void *ptr, + int remains); + +/* return codes for CURLOPT_CHUNK_END_FUNCTION */ +#define CURL_CHUNK_END_FUNC_OK 0 +#define CURL_CHUNK_END_FUNC_FAIL 1 /* tell the lib to end the task */ + +/* If splitting of data transfer is enabled this callback is called after + download of an individual chunk finished. + Note! After this callback was set then it have to be called FOR ALL chunks. + Even if downloading of this chunk was skipped in CHUNK_BGN_FUNC. + This is the reason why we don't need "transfer_info" parameter in this + callback and we are not interested in "remains" parameter too. */ +typedef long (*curl_chunk_end_callback)(void *ptr); + +/* return codes for FNMATCHFUNCTION */ +#define CURL_FNMATCHFUNC_MATCH 0 /* string corresponds to the pattern */ +#define CURL_FNMATCHFUNC_NOMATCH 1 /* pattern doesn't match the string */ +#define CURL_FNMATCHFUNC_FAIL 2 /* an error occurred */ + +/* callback type for wildcard downloading pattern matching. If the + string matches the pattern, return CURL_FNMATCHFUNC_MATCH value, etc. */ +typedef int (*curl_fnmatch_callback)(void *ptr, + const char *pattern, + const char *string); + +/* These are the return codes for the seek callbacks */ +#define CURL_SEEKFUNC_OK 0 +#define CURL_SEEKFUNC_FAIL 1 /* fail the entire transfer */ +#define CURL_SEEKFUNC_CANTSEEK 2 /* tell libcurl seeking can't be done, so + libcurl might try other means instead */ +typedef int (*curl_seek_callback)(void *instream, + curl_off_t offset, + int origin); /* 'whence' */ + +/* This is a return code for the read callback that, when returned, will + signal libcurl to immediately abort the current transfer. */ +#define CURL_READFUNC_ABORT 0x10000000 +/* This is a return code for the read callback that, when returned, will + signal libcurl to pause sending data on the current transfer. */ +#define CURL_READFUNC_PAUSE 0x10000001 + +typedef size_t (*curl_read_callback)(char *buffer, + size_t size, + size_t nitems, + void *instream); + +typedef enum { + CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */ + CURLSOCKTYPE_ACCEPT, /* socket created by accept() call */ + CURLSOCKTYPE_LAST /* never use */ +} curlsocktype; + +/* The return code from the sockopt_callback can signal information back + to libcurl: */ +#define CURL_SOCKOPT_OK 0 +#define CURL_SOCKOPT_ERROR 1 /* causes libcurl to abort and return + CURLE_ABORTED_BY_CALLBACK */ +#define CURL_SOCKOPT_ALREADY_CONNECTED 2 + +typedef int (*curl_sockopt_callback)(void *clientp, + curl_socket_t curlfd, + curlsocktype purpose); + +struct curl_sockaddr { + int family; + int socktype; + int protocol; + unsigned int addrlen; /* addrlen was a socklen_t type before 7.18.0 but it + turned really ugly and painful on the systems that + lack this type */ + struct sockaddr addr; +}; + +typedef curl_socket_t +(*curl_opensocket_callback)(void *clientp, + curlsocktype purpose, + struct curl_sockaddr *address); + +typedef int +(*curl_closesocket_callback)(void *clientp, curl_socket_t item); + +typedef enum { + CURLIOE_OK, /* I/O operation successful */ + CURLIOE_UNKNOWNCMD, /* command was unknown to callback */ + CURLIOE_FAILRESTART, /* failed to restart the read */ + CURLIOE_LAST /* never use */ +} curlioerr; + +typedef enum { + CURLIOCMD_NOP, /* no operation */ + CURLIOCMD_RESTARTREAD, /* restart the read stream from start */ + CURLIOCMD_LAST /* never use */ +} curliocmd; + +typedef curlioerr (*curl_ioctl_callback)(CURL *handle, + int cmd, + void *clientp); + +#ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS +/* + * The following typedef's are signatures of malloc, free, realloc, strdup and + * calloc respectively. Function pointers of these types can be passed to the + * curl_global_init_mem() function to set user defined memory management + * callback routines. + */ +typedef void *(*curl_malloc_callback)(size_t size); +typedef void (*curl_free_callback)(void *ptr); +typedef void *(*curl_realloc_callback)(void *ptr, size_t size); +typedef char *(*curl_strdup_callback)(const char *str); +typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size); + +#define CURL_DID_MEMORY_FUNC_TYPEDEFS +#endif + +/* the kind of data that is passed to information_callback*/ +typedef enum { + CURLINFO_TEXT = 0, + CURLINFO_HEADER_IN, /* 1 */ + CURLINFO_HEADER_OUT, /* 2 */ + CURLINFO_DATA_IN, /* 3 */ + CURLINFO_DATA_OUT, /* 4 */ + CURLINFO_SSL_DATA_IN, /* 5 */ + CURLINFO_SSL_DATA_OUT, /* 6 */ + CURLINFO_END +} curl_infotype; + +typedef int (*curl_debug_callback) + (CURL *handle, /* the handle/transfer this concerns */ + curl_infotype type, /* what kind of data */ + char *data, /* points to the data */ + size_t size, /* size of the data pointed to */ + void *userptr); /* whatever the user please */ + +/* All possible error codes from all sorts of curl functions. Future versions + may return other values, stay prepared. + + Always add new return codes last. Never *EVER* remove any. The return + codes must remain the same! + */ + +typedef enum { + CURLE_OK = 0, + CURLE_UNSUPPORTED_PROTOCOL, /* 1 */ + CURLE_FAILED_INIT, /* 2 */ + CURLE_URL_MALFORMAT, /* 3 */ + CURLE_NOT_BUILT_IN, /* 4 - [was obsoleted in August 2007 for + 7.17.0, reused in April 2011 for 7.21.5] */ + CURLE_COULDNT_RESOLVE_PROXY, /* 5 */ + CURLE_COULDNT_RESOLVE_HOST, /* 6 */ + CURLE_COULDNT_CONNECT, /* 7 */ + CURLE_WEIRD_SERVER_REPLY, /* 8 */ + CURLE_REMOTE_ACCESS_DENIED, /* 9 a service was denied by the server + due to lack of access - when login fails + this is not returned. */ + CURLE_FTP_ACCEPT_FAILED, /* 10 - [was obsoleted in April 2006 for + 7.15.4, reused in Dec 2011 for 7.24.0]*/ + CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */ + CURLE_FTP_ACCEPT_TIMEOUT, /* 12 - timeout occurred accepting server + [was obsoleted in August 2007 for 7.17.0, + reused in Dec 2011 for 7.24.0]*/ + CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */ + CURLE_FTP_WEIRD_227_FORMAT, /* 14 */ + CURLE_FTP_CANT_GET_HOST, /* 15 */ + CURLE_HTTP2, /* 16 - A problem in the http2 framing layer. + [was obsoleted in August 2007 for 7.17.0, + reused in July 2014 for 7.38.0] */ + CURLE_FTP_COULDNT_SET_TYPE, /* 17 */ + CURLE_PARTIAL_FILE, /* 18 */ + CURLE_FTP_COULDNT_RETR_FILE, /* 19 */ + CURLE_OBSOLETE20, /* 20 - NOT USED */ + CURLE_QUOTE_ERROR, /* 21 - quote command failure */ + CURLE_HTTP_RETURNED_ERROR, /* 22 */ + CURLE_WRITE_ERROR, /* 23 */ + CURLE_OBSOLETE24, /* 24 - NOT USED */ + CURLE_UPLOAD_FAILED, /* 25 - failed upload "command" */ + CURLE_READ_ERROR, /* 26 - couldn't open/read from file */ + CURLE_OUT_OF_MEMORY, /* 27 */ + /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error + instead of a memory allocation error if CURL_DOES_CONVERSIONS + is defined + */ + CURLE_OPERATION_TIMEDOUT, /* 28 - the timeout time was reached */ + CURLE_OBSOLETE29, /* 29 - NOT USED */ + CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */ + CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */ + CURLE_OBSOLETE32, /* 32 - NOT USED */ + CURLE_RANGE_ERROR, /* 33 - RANGE "command" didn't work */ + CURLE_HTTP_POST_ERROR, /* 34 */ + CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */ + CURLE_BAD_DOWNLOAD_RESUME, /* 36 - couldn't resume download */ + CURLE_FILE_COULDNT_READ_FILE, /* 37 */ + CURLE_LDAP_CANNOT_BIND, /* 38 */ + CURLE_LDAP_SEARCH_FAILED, /* 39 */ + CURLE_OBSOLETE40, /* 40 - NOT USED */ + CURLE_FUNCTION_NOT_FOUND, /* 41 - NOT USED starting with 7.53.0 */ + CURLE_ABORTED_BY_CALLBACK, /* 42 */ + CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */ + CURLE_OBSOLETE44, /* 44 - NOT USED */ + CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */ + CURLE_OBSOLETE46, /* 46 - NOT USED */ + CURLE_TOO_MANY_REDIRECTS, /* 47 - catch endless re-direct loops */ + CURLE_UNKNOWN_OPTION, /* 48 - User specified an unknown option */ + CURLE_TELNET_OPTION_SYNTAX, /* 49 - Malformed telnet option */ + CURLE_OBSOLETE50, /* 50 - NOT USED */ + CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint + wasn't verified fine */ + CURLE_GOT_NOTHING, /* 52 - when this is a specific error */ + CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */ + CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as + default */ + CURLE_SEND_ERROR, /* 55 - failed sending network data */ + CURLE_RECV_ERROR, /* 56 - failure in receiving network data */ + CURLE_OBSOLETE57, /* 57 - NOT IN USE */ + CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */ + CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */ + CURLE_SSL_CACERT, /* 60 - problem with the CA cert (path?) */ + CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized/bad encoding */ + CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */ + CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ + CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ + CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind + that failed */ + CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ + CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not + accepted and we failed to login */ + CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ + CURLE_TFTP_PERM, /* 69 - permission problem on server */ + CURLE_REMOTE_DISK_FULL, /* 70 - out of disk space on server */ + CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */ + CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */ + CURLE_REMOTE_FILE_EXISTS, /* 73 - File already exists */ + CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */ + CURLE_CONV_FAILED, /* 75 - conversion failed */ + CURLE_CONV_REQD, /* 76 - caller must register conversion + callbacks using curl_easy_setopt options + CURLOPT_CONV_FROM_NETWORK_FUNCTION, + CURLOPT_CONV_TO_NETWORK_FUNCTION, and + CURLOPT_CONV_FROM_UTF8_FUNCTION */ + CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing + or wrong format */ + CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ + CURLE_SSH, /* 79 - error from the SSH layer, somewhat + generic so the error message will be of + interest when this has happened */ + + CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL + connection */ + CURLE_AGAIN, /* 81 - socket is not ready for send/recv, + wait till it's ready and try again (Added + in 7.18.2) */ + CURLE_SSL_CRL_BADFILE, /* 82 - could not load CRL file, missing or + wrong format (Added in 7.19.0) */ + CURLE_SSL_ISSUER_ERROR, /* 83 - Issuer check failed. (Added in + 7.19.0) */ + CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */ + CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers */ + CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ids */ + CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */ + CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ + CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the + session will be queued */ + CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not + match */ + CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */ + CURLE_HTTP2_STREAM, /* 92 - stream error in HTTP/2 framing layer + */ + CURL_LAST /* never use! */ +} CURLcode; + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Previously obsolete error code re-used in 7.38.0 */ +#define CURLE_OBSOLETE16 CURLE_HTTP2 + +/* Previously obsolete error codes re-used in 7.24.0 */ +#define CURLE_OBSOLETE10 CURLE_FTP_ACCEPT_FAILED +#define CURLE_OBSOLETE12 CURLE_FTP_ACCEPT_TIMEOUT + +/* compatibility with older names */ +#define CURLOPT_ENCODING CURLOPT_ACCEPT_ENCODING +#define CURLE_FTP_WEIRD_SERVER_REPLY CURLE_WEIRD_SERVER_REPLY + +/* The following were added in 7.21.5, April 2011 */ +#define CURLE_UNKNOWN_TELNET_OPTION CURLE_UNKNOWN_OPTION + +/* The following were added in 7.17.1 */ +/* These are scheduled to disappear by 2009 */ +#define CURLE_SSL_PEER_CERTIFICATE CURLE_PEER_FAILED_VERIFICATION + +/* The following were added in 7.17.0 */ +/* These are scheduled to disappear by 2009 */ +#define CURLE_OBSOLETE CURLE_OBSOLETE50 /* no one should be using this! */ +#define CURLE_BAD_PASSWORD_ENTERED CURLE_OBSOLETE46 +#define CURLE_BAD_CALLING_ORDER CURLE_OBSOLETE44 +#define CURLE_FTP_USER_PASSWORD_INCORRECT CURLE_OBSOLETE10 +#define CURLE_FTP_CANT_RECONNECT CURLE_OBSOLETE16 +#define CURLE_FTP_COULDNT_GET_SIZE CURLE_OBSOLETE32 +#define CURLE_FTP_COULDNT_SET_ASCII CURLE_OBSOLETE29 +#define CURLE_FTP_WEIRD_USER_REPLY CURLE_OBSOLETE12 +#define CURLE_FTP_WRITE_ERROR CURLE_OBSOLETE20 +#define CURLE_LIBRARY_NOT_FOUND CURLE_OBSOLETE40 +#define CURLE_MALFORMAT_USER CURLE_OBSOLETE24 +#define CURLE_SHARE_IN_USE CURLE_OBSOLETE57 +#define CURLE_URL_MALFORMAT_USER CURLE_NOT_BUILT_IN + +#define CURLE_FTP_ACCESS_DENIED CURLE_REMOTE_ACCESS_DENIED +#define CURLE_FTP_COULDNT_SET_BINARY CURLE_FTP_COULDNT_SET_TYPE +#define CURLE_FTP_QUOTE_ERROR CURLE_QUOTE_ERROR +#define CURLE_TFTP_DISKFULL CURLE_REMOTE_DISK_FULL +#define CURLE_TFTP_EXISTS CURLE_REMOTE_FILE_EXISTS +#define CURLE_HTTP_RANGE_ERROR CURLE_RANGE_ERROR +#define CURLE_FTP_SSL_FAILED CURLE_USE_SSL_FAILED + +/* The following were added earlier */ + +#define CURLE_OPERATION_TIMEOUTED CURLE_OPERATION_TIMEDOUT + +#define CURLE_HTTP_NOT_FOUND CURLE_HTTP_RETURNED_ERROR +#define CURLE_HTTP_PORT_FAILED CURLE_INTERFACE_FAILED +#define CURLE_FTP_COULDNT_STOR_FILE CURLE_UPLOAD_FAILED + +#define CURLE_FTP_PARTIAL_FILE CURLE_PARTIAL_FILE +#define CURLE_FTP_BAD_DOWNLOAD_RESUME CURLE_BAD_DOWNLOAD_RESUME + +/* This was the error code 50 in 7.7.3 and a few earlier versions, this + is no longer used by libcurl but is instead #defined here only to not + make programs break */ +#define CURLE_ALREADY_COMPLETE 99999 + +/* Provide defines for really old option names */ +#define CURLOPT_FILE CURLOPT_WRITEDATA /* name changed in 7.9.7 */ +#define CURLOPT_INFILE CURLOPT_READDATA /* name changed in 7.9.7 */ +#define CURLOPT_WRITEHEADER CURLOPT_HEADERDATA + +/* Since long deprecated options with no code in the lib that does anything + with them. */ +#define CURLOPT_WRITEINFO CURLOPT_OBSOLETE40 +#define CURLOPT_CLOSEPOLICY CURLOPT_OBSOLETE72 + +#endif /*!CURL_NO_OLDIES*/ + +/* This prototype applies to all conversion callbacks */ +typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length); + +typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ + void *ssl_ctx, /* actually an + OpenSSL SSL_CTX */ + void *userptr); + +typedef enum { + CURLPROXY_HTTP = 0, /* added in 7.10, new in 7.19.4 default is to use + CONNECT HTTP/1.1 */ + CURLPROXY_HTTP_1_0 = 1, /* added in 7.19.4, force to use CONNECT + HTTP/1.0 */ + CURLPROXY_HTTPS = 2, /* added in 7.52.0 */ + CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already + in 7.10 */ + CURLPROXY_SOCKS5 = 5, /* added in 7.10 */ + CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */ + CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the + host name rather than the IP address. added + in 7.18.0 */ +} curl_proxytype; /* this enum was added in 7.10 */ + +/* + * Bitmasks for CURLOPT_HTTPAUTH and CURLOPT_PROXYAUTH options: + * + * CURLAUTH_NONE - No HTTP authentication + * CURLAUTH_BASIC - HTTP Basic authentication (default) + * CURLAUTH_DIGEST - HTTP Digest authentication + * CURLAUTH_NEGOTIATE - HTTP Negotiate (SPNEGO) authentication + * CURLAUTH_GSSNEGOTIATE - Alias for CURLAUTH_NEGOTIATE (deprecated) + * CURLAUTH_NTLM - HTTP NTLM authentication + * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavour + * CURLAUTH_NTLM_WB - HTTP NTLM authentication delegated to winbind helper + * CURLAUTH_ONLY - Use together with a single other type to force no + * authentication or just that single type + * CURLAUTH_ANY - All fine types set + * CURLAUTH_ANYSAFE - All fine types except Basic + */ + +#define CURLAUTH_NONE ((unsigned long)0) +#define CURLAUTH_BASIC (((unsigned long)1)<<0) +#define CURLAUTH_DIGEST (((unsigned long)1)<<1) +#define CURLAUTH_NEGOTIATE (((unsigned long)1)<<2) +/* Deprecated since the advent of CURLAUTH_NEGOTIATE */ +#define CURLAUTH_GSSNEGOTIATE CURLAUTH_NEGOTIATE +/* Used for CURLOPT_SOCKS5_AUTH to stay terminologically correct */ +#define CURLAUTH_GSSAPI CURLAUTH_NEGOTIATE +#define CURLAUTH_NTLM (((unsigned long)1)<<3) +#define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) +#define CURLAUTH_NTLM_WB (((unsigned long)1)<<5) +#define CURLAUTH_ONLY (((unsigned long)1)<<31) +#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) +#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE)) + +#define CURLSSH_AUTH_ANY ~0 /* all types supported by the server */ +#define CURLSSH_AUTH_NONE 0 /* none allowed, silly but complete */ +#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */ +#define CURLSSH_AUTH_PASSWORD (1<<1) /* password */ +#define CURLSSH_AUTH_HOST (1<<2) /* host key files */ +#define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */ +#define CURLSSH_AUTH_AGENT (1<<4) /* agent (ssh-agent, pageant...) */ +#define CURLSSH_AUTH_GSSAPI (1<<5) /* gssapi (kerberos, ...) */ +#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY + +#define CURLGSSAPI_DELEGATION_NONE 0 /* no delegation (default) */ +#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1<<0) /* if permitted by policy */ +#define CURLGSSAPI_DELEGATION_FLAG (1<<1) /* delegate always */ + +#define CURL_ERROR_SIZE 256 + +enum curl_khtype { + CURLKHTYPE_UNKNOWN, + CURLKHTYPE_RSA1, + CURLKHTYPE_RSA, + CURLKHTYPE_DSS, + CURLKHTYPE_ECDSA, + CURLKHTYPE_ED25519 +}; + +struct curl_khkey { + const char *key; /* points to a zero-terminated string encoded with base64 + if len is zero, otherwise to the "raw" data */ + size_t len; + enum curl_khtype keytype; +}; + +/* this is the set of return values expected from the curl_sshkeycallback + callback */ +enum curl_khstat { + CURLKHSTAT_FINE_ADD_TO_FILE, + CURLKHSTAT_FINE, + CURLKHSTAT_REJECT, /* reject the connection, return an error */ + CURLKHSTAT_DEFER, /* do not accept it, but we can't answer right now so + this causes a CURLE_DEFER error but otherwise the + connection will be left intact etc */ + CURLKHSTAT_LAST /* not for use, only a marker for last-in-list */ +}; + +/* this is the set of status codes pass in to the callback */ +enum curl_khmatch { + CURLKHMATCH_OK, /* match */ + CURLKHMATCH_MISMATCH, /* host found, key mismatch! */ + CURLKHMATCH_MISSING, /* no matching host/key found */ + CURLKHMATCH_LAST /* not for use, only a marker for last-in-list */ +}; + +typedef int + (*curl_sshkeycallback) (CURL *easy, /* easy handle */ + const struct curl_khkey *knownkey, /* known */ + const struct curl_khkey *foundkey, /* found */ + enum curl_khmatch, /* libcurl's view on the keys */ + void *clientp); /* custom pointer passed from app */ + +/* parameter for the CURLOPT_USE_SSL option */ +typedef enum { + CURLUSESSL_NONE, /* do not attempt to use SSL */ + CURLUSESSL_TRY, /* try using SSL, proceed anyway otherwise */ + CURLUSESSL_CONTROL, /* SSL for the control connection or fail */ + CURLUSESSL_ALL, /* SSL for all communication or fail */ + CURLUSESSL_LAST /* not an option, never use */ +} curl_usessl; + +/* Definition of bits for the CURLOPT_SSL_OPTIONS argument: */ + +/* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the + name of improving interoperability with older servers. Some SSL libraries + have introduced work-arounds for this flaw but those work-arounds sometimes + make the SSL communication fail. To regain functionality with those broken + servers, a user can this way allow the vulnerability back. */ +#define CURLSSLOPT_ALLOW_BEAST (1<<0) + +/* - NO_REVOKE tells libcurl to disable certificate revocation checks for those + SSL backends where such behavior is present. */ +#define CURLSSLOPT_NO_REVOKE (1<<1) + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Backwards compatibility with older names */ +/* These are scheduled to disappear by 2009 */ + +#define CURLFTPSSL_NONE CURLUSESSL_NONE +#define CURLFTPSSL_TRY CURLUSESSL_TRY +#define CURLFTPSSL_CONTROL CURLUSESSL_CONTROL +#define CURLFTPSSL_ALL CURLUSESSL_ALL +#define CURLFTPSSL_LAST CURLUSESSL_LAST +#define curl_ftpssl curl_usessl +#endif /*!CURL_NO_OLDIES*/ + +/* parameter for the CURLOPT_FTP_SSL_CCC option */ +typedef enum { + CURLFTPSSL_CCC_NONE, /* do not send CCC */ + CURLFTPSSL_CCC_PASSIVE, /* Let the server initiate the shutdown */ + CURLFTPSSL_CCC_ACTIVE, /* Initiate the shutdown */ + CURLFTPSSL_CCC_LAST /* not an option, never use */ +} curl_ftpccc; + +/* parameter for the CURLOPT_FTPSSLAUTH option */ +typedef enum { + CURLFTPAUTH_DEFAULT, /* let libcurl decide */ + CURLFTPAUTH_SSL, /* use "AUTH SSL" */ + CURLFTPAUTH_TLS, /* use "AUTH TLS" */ + CURLFTPAUTH_LAST /* not an option, never use */ +} curl_ftpauth; + +/* parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */ +typedef enum { + CURLFTP_CREATE_DIR_NONE, /* do NOT create missing dirs! */ + CURLFTP_CREATE_DIR, /* (FTP/SFTP) if CWD fails, try MKD and then CWD + again if MKD succeeded, for SFTP this does + similar magic */ + CURLFTP_CREATE_DIR_RETRY, /* (FTP only) if CWD fails, try MKD and then CWD + again even if MKD failed! */ + CURLFTP_CREATE_DIR_LAST /* not an option, never use */ +} curl_ftpcreatedir; + +/* parameter for the CURLOPT_FTP_FILEMETHOD option */ +typedef enum { + CURLFTPMETHOD_DEFAULT, /* let libcurl pick */ + CURLFTPMETHOD_MULTICWD, /* single CWD operation for each path part */ + CURLFTPMETHOD_NOCWD, /* no CWD at all */ + CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */ + CURLFTPMETHOD_LAST /* not an option, never use */ +} curl_ftpmethod; + +/* bitmask defines for CURLOPT_HEADEROPT */ +#define CURLHEADER_UNIFIED 0 +#define CURLHEADER_SEPARATE (1<<0) + +/* CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */ +#define CURLPROTO_HTTP (1<<0) +#define CURLPROTO_HTTPS (1<<1) +#define CURLPROTO_FTP (1<<2) +#define CURLPROTO_FTPS (1<<3) +#define CURLPROTO_SCP (1<<4) +#define CURLPROTO_SFTP (1<<5) +#define CURLPROTO_TELNET (1<<6) +#define CURLPROTO_LDAP (1<<7) +#define CURLPROTO_LDAPS (1<<8) +#define CURLPROTO_DICT (1<<9) +#define CURLPROTO_FILE (1<<10) +#define CURLPROTO_TFTP (1<<11) +#define CURLPROTO_IMAP (1<<12) +#define CURLPROTO_IMAPS (1<<13) +#define CURLPROTO_POP3 (1<<14) +#define CURLPROTO_POP3S (1<<15) +#define CURLPROTO_SMTP (1<<16) +#define CURLPROTO_SMTPS (1<<17) +#define CURLPROTO_RTSP (1<<18) +#define CURLPROTO_RTMP (1<<19) +#define CURLPROTO_RTMPT (1<<20) +#define CURLPROTO_RTMPE (1<<21) +#define CURLPROTO_RTMPTE (1<<22) +#define CURLPROTO_RTMPS (1<<23) +#define CURLPROTO_RTMPTS (1<<24) +#define CURLPROTO_GOPHER (1<<25) +#define CURLPROTO_SMB (1<<26) +#define CURLPROTO_SMBS (1<<27) +#define CURLPROTO_ALL (~0) /* enable everything */ + +/* long may be 32 or 64 bits, but we should never depend on anything else + but 32 */ +#define CURLOPTTYPE_LONG 0 +#define CURLOPTTYPE_OBJECTPOINT 10000 +#define CURLOPTTYPE_STRINGPOINT 10000 +#define CURLOPTTYPE_FUNCTIONPOINT 20000 +#define CURLOPTTYPE_OFF_T 30000 + +/* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the + string options from the header file */ + +/* name is uppercase CURLOPT_, + type is one of the defined CURLOPTTYPE_ + number is unique identifier */ +#ifdef CINIT +#undef CINIT +#endif + +#ifdef CURL_ISOCPP +#define CINIT(na,t,nu) CURLOPT_ ## na = CURLOPTTYPE_ ## t + nu +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define LONG CURLOPTTYPE_LONG +#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define STRINGPOINT CURLOPTTYPE_OBJECTPOINT +#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT +#define OFF_T CURLOPTTYPE_OFF_T +#define CINIT(name,type,number) CURLOPT_/**/name = type + number +#endif + +/* + * This macro-mania below setups the CURLOPT_[what] enum, to be used with + * curl_easy_setopt(). The first argument in the CINIT() macro is the [what] + * word. + */ + +typedef enum { + /* This is the FILE * or void * the regular output should be written to. */ + CINIT(WRITEDATA, OBJECTPOINT, 1), + + /* The full URL to get/put */ + CINIT(URL, STRINGPOINT, 2), + + /* Port number to connect to, if other than default. */ + CINIT(PORT, LONG, 3), + + /* Name of proxy to use. */ + CINIT(PROXY, STRINGPOINT, 4), + + /* "user:password;options" to use when fetching. */ + CINIT(USERPWD, STRINGPOINT, 5), + + /* "user:password" to use with proxy. */ + CINIT(PROXYUSERPWD, STRINGPOINT, 6), + + /* Range to get, specified as an ASCII string. */ + CINIT(RANGE, STRINGPOINT, 7), + + /* not used */ + + /* Specified file stream to upload from (use as input): */ + CINIT(READDATA, OBJECTPOINT, 9), + + /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE + * bytes big. */ + CINIT(ERRORBUFFER, OBJECTPOINT, 10), + + /* Function that will be called to store the output (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11), + + /* Function that will be called to read the input (instead of fread). The + * parameters will use fread() syntax, make sure to follow them. */ + CINIT(READFUNCTION, FUNCTIONPOINT, 12), + + /* Time-out the read operation after this amount of seconds */ + CINIT(TIMEOUT, LONG, 13), + + /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about + * how large the file being sent really is. That allows better error + * checking and better verifies that the upload was successful. -1 means + * unknown size. + * + * For large file support, there is also a _LARGE version of the key + * which takes an off_t type, allowing platforms with larger off_t + * sizes to handle larger files. See below for INFILESIZE_LARGE. + */ + CINIT(INFILESIZE, LONG, 14), + + /* POST static input fields. */ + CINIT(POSTFIELDS, OBJECTPOINT, 15), + + /* Set the referrer page (needed by some CGIs) */ + CINIT(REFERER, STRINGPOINT, 16), + + /* Set the FTP PORT string (interface name, named or numerical IP address) + Use i.e '-' to use default address. */ + CINIT(FTPPORT, STRINGPOINT, 17), + + /* Set the User-Agent string (examined by some CGIs) */ + CINIT(USERAGENT, STRINGPOINT, 18), + + /* If the download receives less than "low speed limit" bytes/second + * during "low speed time" seconds, the operations is aborted. + * You could i.e if you have a pretty high speed connection, abort if + * it is less than 2000 bytes/sec during 20 seconds. + */ + + /* Set the "low speed limit" */ + CINIT(LOW_SPEED_LIMIT, LONG, 19), + + /* Set the "low speed time" */ + CINIT(LOW_SPEED_TIME, LONG, 20), + + /* Set the continuation offset. + * + * Note there is also a _LARGE version of this key which uses + * off_t types, allowing for large file offsets on platforms which + * use larger-than-32-bit off_t's. Look below for RESUME_FROM_LARGE. + */ + CINIT(RESUME_FROM, LONG, 21), + + /* Set cookie in request: */ + CINIT(COOKIE, STRINGPOINT, 22), + + /* This points to a linked list of headers, struct curl_slist kind. This + list is also used for RTSP (in spite of its name) */ + CINIT(HTTPHEADER, OBJECTPOINT, 23), + + /* This points to a linked list of post entries, struct curl_httppost */ + CINIT(HTTPPOST, OBJECTPOINT, 24), + + /* name of the file keeping your private SSL-certificate */ + CINIT(SSLCERT, STRINGPOINT, 25), + + /* password for the SSL or SSH private key */ + CINIT(KEYPASSWD, STRINGPOINT, 26), + + /* send TYPE parameter? */ + CINIT(CRLF, LONG, 27), + + /* send linked-list of QUOTE commands */ + CINIT(QUOTE, OBJECTPOINT, 28), + + /* send FILE * or void * to store headers to, if you use a callback it + is simply passed to the callback unmodified */ + CINIT(HEADERDATA, OBJECTPOINT, 29), + + /* point to a file to read the initial cookies from, also enables + "cookie awareness" */ + CINIT(COOKIEFILE, STRINGPOINT, 31), + + /* What version to specifically try to use. + See CURL_SSLVERSION defines below. */ + CINIT(SSLVERSION, LONG, 32), + + /* What kind of HTTP time condition to use, see defines */ + CINIT(TIMECONDITION, LONG, 33), + + /* Time to use with the above condition. Specified in number of seconds + since 1 Jan 1970 */ + CINIT(TIMEVALUE, LONG, 34), + + /* 35 = OBSOLETE */ + + /* Custom request, for customizing the get command like + HTTP: DELETE, TRACE and others + FTP: to use a different list command + */ + CINIT(CUSTOMREQUEST, STRINGPOINT, 36), + + /* FILE handle to use instead of stderr */ + CINIT(STDERR, OBJECTPOINT, 37), + + /* 38 is not used */ + + /* send linked-list of post-transfer QUOTE commands */ + CINIT(POSTQUOTE, OBJECTPOINT, 39), + + CINIT(OBSOLETE40, OBJECTPOINT, 40), /* OBSOLETE, do not use! */ + + CINIT(VERBOSE, LONG, 41), /* talk a lot */ + CINIT(HEADER, LONG, 42), /* throw the header out too */ + CINIT(NOPROGRESS, LONG, 43), /* shut off the progress meter */ + CINIT(NOBODY, LONG, 44), /* use HEAD to get http document */ + CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 400 */ + CINIT(UPLOAD, LONG, 46), /* this is an upload */ + CINIT(POST, LONG, 47), /* HTTP POST method */ + CINIT(DIRLISTONLY, LONG, 48), /* bare names when listing directories */ + + CINIT(APPEND, LONG, 50), /* Append instead of overwrite on upload! */ + + /* Specify whether to read the user+password from the .netrc or the URL. + * This must be one of the CURL_NETRC_* enums below. */ + CINIT(NETRC, LONG, 51), + + CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */ + + CINIT(TRANSFERTEXT, LONG, 53), /* transfer data in text/ASCII format */ + CINIT(PUT, LONG, 54), /* HTTP PUT */ + + /* 55 = OBSOLETE */ + + /* DEPRECATED + * Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_progress_callback + * prototype defines. */ + CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56), + + /* Data passed to the CURLOPT_PROGRESSFUNCTION and CURLOPT_XFERINFOFUNCTION + callbacks */ + CINIT(PROGRESSDATA, OBJECTPOINT, 57), +#define CURLOPT_XFERINFODATA CURLOPT_PROGRESSDATA + + /* We want the referrer field set automatically when following locations */ + CINIT(AUTOREFERER, LONG, 58), + + /* Port of the proxy, can be set in the proxy string as well with: + "[host]:[port]" */ + CINIT(PROXYPORT, LONG, 59), + + /* size of the POST input data, if strlen() is not good to use */ + CINIT(POSTFIELDSIZE, LONG, 60), + + /* tunnel non-http operations through a HTTP proxy */ + CINIT(HTTPPROXYTUNNEL, LONG, 61), + + /* Set the interface string to use as outgoing network interface */ + CINIT(INTERFACE, STRINGPOINT, 62), + + /* Set the krb4/5 security level, this also enables krb4/5 awareness. This + * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string + * is set but doesn't match one of these, 'private' will be used. */ + CINIT(KRBLEVEL, STRINGPOINT, 63), + + /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ + CINIT(SSL_VERIFYPEER, LONG, 64), + + /* The CApath or CAfile used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAINFO, STRINGPOINT, 65), + + /* 66 = OBSOLETE */ + /* 67 = OBSOLETE */ + + /* Maximum number of http redirects to follow */ + CINIT(MAXREDIRS, LONG, 68), + + /* Pass a long set to 1 to get the date of the requested document (if + possible)! Pass a zero to shut it off. */ + CINIT(FILETIME, LONG, 69), + + /* This points to a linked list of telnet options */ + CINIT(TELNETOPTIONS, OBJECTPOINT, 70), + + /* Max amount of cached alive connections */ + CINIT(MAXCONNECTS, LONG, 71), + + CINIT(OBSOLETE72, LONG, 72), /* OBSOLETE, do not use! */ + + /* 73 = OBSOLETE */ + + /* Set to explicitly use a new connection for the upcoming transfer. + Do not use this unless you're absolutely sure of this, as it makes the + operation slower and is less friendly for the network. */ + CINIT(FRESH_CONNECT, LONG, 74), + + /* Set to explicitly forbid the upcoming transfer's connection to be re-used + when done. Do not use this unless you're absolutely sure of this, as it + makes the operation slower and is less friendly for the network. */ + CINIT(FORBID_REUSE, LONG, 75), + + /* Set to a file name that contains random data for libcurl to use to + seed the random engine when doing SSL connects. */ + CINIT(RANDOM_FILE, STRINGPOINT, 76), + + /* Set to the Entropy Gathering Daemon socket pathname */ + CINIT(EGDSOCKET, STRINGPOINT, 77), + + /* Time-out connect operations after this amount of seconds, if connects are + OK within this time, then fine... This only aborts the connect phase. */ + CINIT(CONNECTTIMEOUT, LONG, 78), + + /* Function that will be called to store headers (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79), + + /* Set this to force the HTTP request to get back to GET. Only really usable + if POST, PUT or a custom request have been used first. + */ + CINIT(HTTPGET, LONG, 80), + + /* Set if we should verify the Common name from the peer certificate in ssl + * handshake, set 1 to check existence, 2 to ensure that it matches the + * provided hostname. */ + CINIT(SSL_VERIFYHOST, LONG, 81), + + /* Specify which file name to write all known cookies in after completed + operation. Set file name to "-" (dash) to make it go to stdout. */ + CINIT(COOKIEJAR, STRINGPOINT, 82), + + /* Specify which SSL ciphers to use */ + CINIT(SSL_CIPHER_LIST, STRINGPOINT, 83), + + /* Specify which HTTP version to use! This must be set to one of the + CURL_HTTP_VERSION* enums set below. */ + CINIT(HTTP_VERSION, LONG, 84), + + /* Specifically switch on or off the FTP engine's use of the EPSV command. By + default, that one will always be attempted before the more traditional + PASV command. */ + CINIT(FTP_USE_EPSV, LONG, 85), + + /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */ + CINIT(SSLCERTTYPE, STRINGPOINT, 86), + + /* name of the file keeping your private SSL-key */ + CINIT(SSLKEY, STRINGPOINT, 87), + + /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */ + CINIT(SSLKEYTYPE, STRINGPOINT, 88), + + /* crypto engine for the SSL-sub system */ + CINIT(SSLENGINE, STRINGPOINT, 89), + + /* set the crypto engine for the SSL-sub system as default + the param has no meaning... + */ + CINIT(SSLENGINE_DEFAULT, LONG, 90), + + /* Non-zero value means to use the global dns cache */ + CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* DEPRECATED, do not use! */ + + /* DNS cache timeout */ + CINIT(DNS_CACHE_TIMEOUT, LONG, 92), + + /* send linked-list of pre-transfer QUOTE commands */ + CINIT(PREQUOTE, OBJECTPOINT, 93), + + /* set the debug function */ + CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94), + + /* set the data for the debug function */ + CINIT(DEBUGDATA, OBJECTPOINT, 95), + + /* mark this as start of a cookie session */ + CINIT(COOKIESESSION, LONG, 96), + + /* The CApath directory used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAPATH, STRINGPOINT, 97), + + /* Instruct libcurl to use a smaller receive buffer */ + CINIT(BUFFERSIZE, LONG, 98), + + /* Instruct libcurl to not use any signal/alarm handlers, even when using + timeouts. This option is useful for multi-threaded applications. + See libcurl-the-guide for more background information. */ + CINIT(NOSIGNAL, LONG, 99), + + /* Provide a CURLShare for mutexing non-ts data */ + CINIT(SHARE, OBJECTPOINT, 100), + + /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), + CURLPROXY_HTTPS, CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and + CURLPROXY_SOCKS5. */ + CINIT(PROXYTYPE, LONG, 101), + + /* Set the Accept-Encoding string. Use this to tell a server you would like + the response to be compressed. Before 7.21.6, this was known as + CURLOPT_ENCODING */ + CINIT(ACCEPT_ENCODING, STRINGPOINT, 102), + + /* Set pointer to private data */ + CINIT(PRIVATE, OBJECTPOINT, 103), + + /* Set aliases for HTTP 200 in the HTTP Response header */ + CINIT(HTTP200ALIASES, OBJECTPOINT, 104), + + /* Continue to send authentication (user+password) when following locations, + even when hostname changed. This can potentially send off the name + and password to whatever host the server decides. */ + CINIT(UNRESTRICTED_AUTH, LONG, 105), + + /* Specifically switch on or off the FTP engine's use of the EPRT command ( + it also disables the LPRT attempt). By default, those ones will always be + attempted before the good old traditional PORT command. */ + CINIT(FTP_USE_EPRT, LONG, 106), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_USERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(HTTPAUTH, LONG, 107), + + /* Set the ssl context callback function, currently only for OpenSSL ssl_ctx + in second argument. The function must be matching the + curl_ssl_ctx_callback proto. */ + CINIT(SSL_CTX_FUNCTION, FUNCTIONPOINT, 108), + + /* Set the userdata for the ssl context callback function's third + argument */ + CINIT(SSL_CTX_DATA, OBJECTPOINT, 109), + + /* FTP Option that causes missing dirs to be created on the remote server. + In 7.19.4 we introduced the convenience enums for this option using the + CURLFTP_CREATE_DIR prefix. + */ + CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_PROXYUSERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(PROXYAUTH, LONG, 111), + + /* FTP option that changes the timeout, in seconds, associated with + getting a response. This is different from transfer timeout time and + essentially places a demand on the FTP server to acknowledge commands + in a timely manner. */ + CINIT(FTP_RESPONSE_TIMEOUT, LONG, 112), +#define CURLOPT_SERVER_RESPONSE_TIMEOUT CURLOPT_FTP_RESPONSE_TIMEOUT + + /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to + tell libcurl to resolve names to those IP versions only. This only has + affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */ + CINIT(IPRESOLVE, LONG, 113), + + /* Set this option to limit the size of a file that will be downloaded from + an HTTP or FTP server. + + Note there is also _LARGE version which adds large file support for + platforms which have larger off_t sizes. See MAXFILESIZE_LARGE below. */ + CINIT(MAXFILESIZE, LONG, 114), + + /* See the comment for INFILESIZE above, but in short, specifies + * the size of the file being uploaded. -1 means unknown. + */ + CINIT(INFILESIZE_LARGE, OFF_T, 115), + + /* Sets the continuation offset. There is also a LONG version of this; + * look above for RESUME_FROM. + */ + CINIT(RESUME_FROM_LARGE, OFF_T, 116), + + /* Sets the maximum size of data that will be downloaded from + * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. + */ + CINIT(MAXFILESIZE_LARGE, OFF_T, 117), + + /* Set this option to the file name of your .netrc file you want libcurl + to parse (using the CURLOPT_NETRC option). If not set, libcurl will do + a poor attempt to find the user's home directory and check for a .netrc + file in there. */ + CINIT(NETRC_FILE, STRINGPOINT, 118), + + /* Enable SSL/TLS for FTP, pick one of: + CURLUSESSL_TRY - try using SSL, proceed anyway otherwise + CURLUSESSL_CONTROL - SSL for the control connection or fail + CURLUSESSL_ALL - SSL for all communication or fail + */ + CINIT(USE_SSL, LONG, 119), + + /* The _LARGE version of the standard POSTFIELDSIZE option */ + CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120), + + /* Enable/disable the TCP Nagle algorithm */ + CINIT(TCP_NODELAY, LONG, 121), + + /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 123 OBSOLETE. Gone in 7.16.0 */ + /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 127 OBSOLETE. Gone in 7.16.0 */ + /* 128 OBSOLETE. Gone in 7.16.0 */ + + /* When FTP over SSL/TLS is selected (with CURLOPT_USE_SSL), this option + can be used to change libcurl's default action which is to first try + "AUTH SSL" and then "AUTH TLS" in this order, and proceed when a OK + response has been received. + + Available parameters are: + CURLFTPAUTH_DEFAULT - let libcurl decide + CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS + CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL + */ + CINIT(FTPSSLAUTH, LONG, 129), + + CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130), + CINIT(IOCTLDATA, OBJECTPOINT, 131), + + /* 132 OBSOLETE. Gone in 7.16.0 */ + /* 133 OBSOLETE. Gone in 7.16.0 */ + + /* zero terminated string for pass on to the FTP server when asked for + "account" info */ + CINIT(FTP_ACCOUNT, STRINGPOINT, 134), + + /* feed cookie into cookie engine */ + CINIT(COOKIELIST, STRINGPOINT, 135), + + /* ignore Content-Length */ + CINIT(IGNORE_CONTENT_LENGTH, LONG, 136), + + /* Set to non-zero to skip the IP address received in a 227 PASV FTP server + response. Typically used for FTP-SSL purposes but is not restricted to + that. libcurl will then instead use the same IP address it used for the + control connection. */ + CINIT(FTP_SKIP_PASV_IP, LONG, 137), + + /* Select "file method" to use when doing FTP, see the curl_ftpmethod + above. */ + CINIT(FTP_FILEMETHOD, LONG, 138), + + /* Local port number to bind the socket to */ + CINIT(LOCALPORT, LONG, 139), + + /* Number of ports to try, including the first one set with LOCALPORT. + Thus, setting it to 1 will make no additional attempts but the first. + */ + CINIT(LOCALPORTRANGE, LONG, 140), + + /* no transfer, set up connection and let application use the socket by + extracting it with CURLINFO_LASTSOCKET */ + CINIT(CONNECT_ONLY, LONG, 141), + + /* Function that will be called to convert from the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_FROM_NETWORK_FUNCTION, FUNCTIONPOINT, 142), + + /* Function that will be called to convert to the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_TO_NETWORK_FUNCTION, FUNCTIONPOINT, 143), + + /* Function that will be called to convert from UTF8 + (instead of using the iconv calls in libcurl) + Note that this is used only for SSL certificate processing */ + CINIT(CONV_FROM_UTF8_FUNCTION, FUNCTIONPOINT, 144), + + /* if the connection proceeds too quickly then need to slow it down */ + /* limit-rate: maximum number of bytes per second to send or receive */ + CINIT(MAX_SEND_SPEED_LARGE, OFF_T, 145), + CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146), + + /* Pointer to command string to send if USER/PASS fails. */ + CINIT(FTP_ALTERNATIVE_TO_USER, STRINGPOINT, 147), + + /* callback function for setting socket options */ + CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148), + CINIT(SOCKOPTDATA, OBJECTPOINT, 149), + + /* set to 0 to disable session ID re-use for this transfer, default is + enabled (== 1) */ + CINIT(SSL_SESSIONID_CACHE, LONG, 150), + + /* allowed SSH authentication methods */ + CINIT(SSH_AUTH_TYPES, LONG, 151), + + /* Used by scp/sftp to do public/private key authentication */ + CINIT(SSH_PUBLIC_KEYFILE, STRINGPOINT, 152), + CINIT(SSH_PRIVATE_KEYFILE, STRINGPOINT, 153), + + /* Send CCC (Clear Command Channel) after authentication */ + CINIT(FTP_SSL_CCC, LONG, 154), + + /* Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */ + CINIT(TIMEOUT_MS, LONG, 155), + CINIT(CONNECTTIMEOUT_MS, LONG, 156), + + /* set to zero to disable the libcurl's decoding and thus pass the raw body + data to the application even when it is encoded/compressed */ + CINIT(HTTP_TRANSFER_DECODING, LONG, 157), + CINIT(HTTP_CONTENT_DECODING, LONG, 158), + + /* Permission used when creating new files and directories on the remote + server for protocols that support it, SFTP/SCP/FILE */ + CINIT(NEW_FILE_PERMS, LONG, 159), + CINIT(NEW_DIRECTORY_PERMS, LONG, 160), + + /* Set the behaviour of POST when redirecting. Values must be set to one + of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */ + CINIT(POSTREDIR, LONG, 161), + + /* used by scp/sftp to verify the host's public key */ + CINIT(SSH_HOST_PUBLIC_KEY_MD5, STRINGPOINT, 162), + + /* Callback function for opening socket (instead of socket(2)). Optionally, + callback is able change the address or refuse to connect returning + CURL_SOCKET_BAD. The callback should have type + curl_opensocket_callback */ + CINIT(OPENSOCKETFUNCTION, FUNCTIONPOINT, 163), + CINIT(OPENSOCKETDATA, OBJECTPOINT, 164), + + /* POST volatile input fields. */ + CINIT(COPYPOSTFIELDS, OBJECTPOINT, 165), + + /* set transfer mode (;type=) when doing FTP via an HTTP proxy */ + CINIT(PROXY_TRANSFER_MODE, LONG, 166), + + /* Callback function for seeking in the input stream */ + CINIT(SEEKFUNCTION, FUNCTIONPOINT, 167), + CINIT(SEEKDATA, OBJECTPOINT, 168), + + /* CRL file */ + CINIT(CRLFILE, STRINGPOINT, 169), + + /* Issuer certificate */ + CINIT(ISSUERCERT, STRINGPOINT, 170), + + /* (IPv6) Address scope */ + CINIT(ADDRESS_SCOPE, LONG, 171), + + /* Collect certificate chain info and allow it to get retrievable with + CURLINFO_CERTINFO after the transfer is complete. */ + CINIT(CERTINFO, LONG, 172), + + /* "name" and "pwd" to use when fetching. */ + CINIT(USERNAME, STRINGPOINT, 173), + CINIT(PASSWORD, STRINGPOINT, 174), + + /* "name" and "pwd" to use with Proxy when fetching. */ + CINIT(PROXYUSERNAME, STRINGPOINT, 175), + CINIT(PROXYPASSWORD, STRINGPOINT, 176), + + /* Comma separated list of hostnames defining no-proxy zones. These should + match both hostnames directly, and hostnames within a domain. For + example, local.com will match local.com and www.local.com, but NOT + notlocal.com or www.notlocal.com. For compatibility with other + implementations of this, .local.com will be considered to be the same as + local.com. A single * is the only valid wildcard, and effectively + disables the use of proxy. */ + CINIT(NOPROXY, STRINGPOINT, 177), + + /* block size for TFTP transfers */ + CINIT(TFTP_BLKSIZE, LONG, 178), + + /* Socks Service */ + CINIT(SOCKS5_GSSAPI_SERVICE, STRINGPOINT, 179), /* DEPRECATED, do not use! */ + + /* Socks Service */ + CINIT(SOCKS5_GSSAPI_NEC, LONG, 180), + + /* set the bitmask for the protocols that are allowed to be used for the + transfer, which thus helps the app which takes URLs from users or other + external inputs and want to restrict what protocol(s) to deal + with. Defaults to CURLPROTO_ALL. */ + CINIT(PROTOCOLS, LONG, 181), + + /* set the bitmask for the protocols that libcurl is allowed to follow to, + as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs + to be set in both bitmasks to be allowed to get redirected to. Defaults + to all protocols except FILE and SCP. */ + CINIT(REDIR_PROTOCOLS, LONG, 182), + + /* set the SSH knownhost file name to use */ + CINIT(SSH_KNOWNHOSTS, STRINGPOINT, 183), + + /* set the SSH host key callback, must point to a curl_sshkeycallback + function */ + CINIT(SSH_KEYFUNCTION, FUNCTIONPOINT, 184), + + /* set the SSH host key callback custom pointer */ + CINIT(SSH_KEYDATA, OBJECTPOINT, 185), + + /* set the SMTP mail originator */ + CINIT(MAIL_FROM, STRINGPOINT, 186), + + /* set the list of SMTP mail receiver(s) */ + CINIT(MAIL_RCPT, OBJECTPOINT, 187), + + /* FTP: send PRET before PASV */ + CINIT(FTP_USE_PRET, LONG, 188), + + /* RTSP request method (OPTIONS, SETUP, PLAY, etc...) */ + CINIT(RTSP_REQUEST, LONG, 189), + + /* The RTSP session identifier */ + CINIT(RTSP_SESSION_ID, STRINGPOINT, 190), + + /* The RTSP stream URI */ + CINIT(RTSP_STREAM_URI, STRINGPOINT, 191), + + /* The Transport: header to use in RTSP requests */ + CINIT(RTSP_TRANSPORT, STRINGPOINT, 192), + + /* Manually initialize the client RTSP CSeq for this handle */ + CINIT(RTSP_CLIENT_CSEQ, LONG, 193), + + /* Manually initialize the server RTSP CSeq for this handle */ + CINIT(RTSP_SERVER_CSEQ, LONG, 194), + + /* The stream to pass to INTERLEAVEFUNCTION. */ + CINIT(INTERLEAVEDATA, OBJECTPOINT, 195), + + /* Let the application define a custom write method for RTP data */ + CINIT(INTERLEAVEFUNCTION, FUNCTIONPOINT, 196), + + /* Turn on wildcard matching */ + CINIT(WILDCARDMATCH, LONG, 197), + + /* Directory matching callback called before downloading of an + individual file (chunk) started */ + CINIT(CHUNK_BGN_FUNCTION, FUNCTIONPOINT, 198), + + /* Directory matching callback called after the file (chunk) + was downloaded, or skipped */ + CINIT(CHUNK_END_FUNCTION, FUNCTIONPOINT, 199), + + /* Change match (fnmatch-like) callback for wildcard matching */ + CINIT(FNMATCH_FUNCTION, FUNCTIONPOINT, 200), + + /* Let the application define custom chunk data pointer */ + CINIT(CHUNK_DATA, OBJECTPOINT, 201), + + /* FNMATCH_FUNCTION user pointer */ + CINIT(FNMATCH_DATA, OBJECTPOINT, 202), + + /* send linked-list of name:port:address sets */ + CINIT(RESOLVE, OBJECTPOINT, 203), + + /* Set a username for authenticated TLS */ + CINIT(TLSAUTH_USERNAME, STRINGPOINT, 204), + + /* Set a password for authenticated TLS */ + CINIT(TLSAUTH_PASSWORD, STRINGPOINT, 205), + + /* Set authentication type for authenticated TLS */ + CINIT(TLSAUTH_TYPE, STRINGPOINT, 206), + + /* Set to 1 to enable the "TE:" header in HTTP requests to ask for + compressed transfer-encoded responses. Set to 0 to disable the use of TE: + in outgoing requests. The current default is 0, but it might change in a + future libcurl release. + + libcurl will ask for the compressed methods it knows of, and if that + isn't any, it will not ask for transfer-encoding at all even if this + option is set to 1. + + */ + CINIT(TRANSFER_ENCODING, LONG, 207), + + /* Callback function for closing socket (instead of close(2)). The callback + should have type curl_closesocket_callback */ + CINIT(CLOSESOCKETFUNCTION, FUNCTIONPOINT, 208), + CINIT(CLOSESOCKETDATA, OBJECTPOINT, 209), + + /* allow GSSAPI credential delegation */ + CINIT(GSSAPI_DELEGATION, LONG, 210), + + /* Set the name servers to use for DNS resolution */ + CINIT(DNS_SERVERS, STRINGPOINT, 211), + + /* Time-out accept operations (currently for FTP only) after this amount + of milliseconds. */ + CINIT(ACCEPTTIMEOUT_MS, LONG, 212), + + /* Set TCP keepalive */ + CINIT(TCP_KEEPALIVE, LONG, 213), + + /* non-universal keepalive knobs (Linux, AIX, HP-UX, more) */ + CINIT(TCP_KEEPIDLE, LONG, 214), + CINIT(TCP_KEEPINTVL, LONG, 215), + + /* Enable/disable specific SSL features with a bitmask, see CURLSSLOPT_* */ + CINIT(SSL_OPTIONS, LONG, 216), + + /* Set the SMTP auth originator */ + CINIT(MAIL_AUTH, STRINGPOINT, 217), + + /* Enable/disable SASL initial response */ + CINIT(SASL_IR, LONG, 218), + + /* Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_xferinfo_callback + * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */ + CINIT(XFERINFOFUNCTION, FUNCTIONPOINT, 219), + + /* The XOAUTH2 bearer token */ + CINIT(XOAUTH2_BEARER, STRINGPOINT, 220), + + /* Set the interface string to use as outgoing network + * interface for DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_INTERFACE, STRINGPOINT, 221), + + /* Set the local IPv4 address to use for outgoing DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_LOCAL_IP4, STRINGPOINT, 222), + + /* Set the local IPv4 address to use for outgoing DNS requests. + * Only supported by the c-ares DNS backend */ + CINIT(DNS_LOCAL_IP6, STRINGPOINT, 223), + + /* Set authentication options directly */ + CINIT(LOGIN_OPTIONS, STRINGPOINT, 224), + + /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ + CINIT(SSL_ENABLE_NPN, LONG, 225), + + /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ + CINIT(SSL_ENABLE_ALPN, LONG, 226), + + /* Time to wait for a response to a HTTP request containing an + * Expect: 100-continue header before sending the data anyway. */ + CINIT(EXPECT_100_TIMEOUT_MS, LONG, 227), + + /* This points to a linked list of headers used for proxy requests only, + struct curl_slist kind */ + CINIT(PROXYHEADER, OBJECTPOINT, 228), + + /* Pass in a bitmask of "header options" */ + CINIT(HEADEROPT, LONG, 229), + + /* The public key in DER form used to validate the peer public key + this option is used only if SSL_VERIFYPEER is true */ + CINIT(PINNEDPUBLICKEY, STRINGPOINT, 230), + + /* Path to Unix domain socket */ + CINIT(UNIX_SOCKET_PATH, STRINGPOINT, 231), + + /* Set if we should verify the certificate status. */ + CINIT(SSL_VERIFYSTATUS, LONG, 232), + + /* Set if we should enable TLS false start. */ + CINIT(SSL_FALSESTART, LONG, 233), + + /* Do not squash dot-dot sequences */ + CINIT(PATH_AS_IS, LONG, 234), + + /* Proxy Service Name */ + CINIT(PROXY_SERVICE_NAME, STRINGPOINT, 235), + + /* Service Name */ + CINIT(SERVICE_NAME, STRINGPOINT, 236), + + /* Wait/don't wait for pipe/mutex to clarify */ + CINIT(PIPEWAIT, LONG, 237), + + /* Set the protocol used when curl is given a URL without a protocol */ + CINIT(DEFAULT_PROTOCOL, STRINGPOINT, 238), + + /* Set stream weight, 1 - 256 (default is 16) */ + CINIT(STREAM_WEIGHT, LONG, 239), + + /* Set stream dependency on another CURL handle */ + CINIT(STREAM_DEPENDS, OBJECTPOINT, 240), + + /* Set E-xclusive stream dependency on another CURL handle */ + CINIT(STREAM_DEPENDS_E, OBJECTPOINT, 241), + + /* Do not send any tftp option requests to the server */ + CINIT(TFTP_NO_OPTIONS, LONG, 242), + + /* Linked-list of host:port:connect-to-host:connect-to-port, + overrides the URL's host:port (only for the network layer) */ + CINIT(CONNECT_TO, OBJECTPOINT, 243), + + /* Set TCP Fast Open */ + CINIT(TCP_FASTOPEN, LONG, 244), + + /* Continue to send data if the server responds early with an + * HTTP status code >= 300 */ + CINIT(KEEP_SENDING_ON_ERROR, LONG, 245), + + /* The CApath or CAfile used to validate the proxy certificate + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CINIT(PROXY_CAINFO, STRINGPOINT, 246), + + /* The CApath directory used to validate the proxy certificate + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CINIT(PROXY_CAPATH, STRINGPOINT, 247), + + /* Set if we should verify the proxy in ssl handshake, + set 1 to verify. */ + CINIT(PROXY_SSL_VERIFYPEER, LONG, 248), + + /* Set if we should verify the Common name from the proxy certificate in ssl + * handshake, set 1 to check existence, 2 to ensure that it matches + * the provided hostname. */ + CINIT(PROXY_SSL_VERIFYHOST, LONG, 249), + + /* What version to specifically try to use for proxy. + See CURL_SSLVERSION defines below. */ + CINIT(PROXY_SSLVERSION, LONG, 250), + + /* Set a username for authenticated TLS for proxy */ + CINIT(PROXY_TLSAUTH_USERNAME, STRINGPOINT, 251), + + /* Set a password for authenticated TLS for proxy */ + CINIT(PROXY_TLSAUTH_PASSWORD, STRINGPOINT, 252), + + /* Set authentication type for authenticated TLS for proxy */ + CINIT(PROXY_TLSAUTH_TYPE, STRINGPOINT, 253), + + /* name of the file keeping your private SSL-certificate for proxy */ + CINIT(PROXY_SSLCERT, STRINGPOINT, 254), + + /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") for + proxy */ + CINIT(PROXY_SSLCERTTYPE, STRINGPOINT, 255), + + /* name of the file keeping your private SSL-key for proxy */ + CINIT(PROXY_SSLKEY, STRINGPOINT, 256), + + /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") for + proxy */ + CINIT(PROXY_SSLKEYTYPE, STRINGPOINT, 257), + + /* password for the SSL private key for proxy */ + CINIT(PROXY_KEYPASSWD, STRINGPOINT, 258), + + /* Specify which SSL ciphers to use for proxy */ + CINIT(PROXY_SSL_CIPHER_LIST, STRINGPOINT, 259), + + /* CRL file for proxy */ + CINIT(PROXY_CRLFILE, STRINGPOINT, 260), + + /* Enable/disable specific SSL features with a bitmask for proxy, see + CURLSSLOPT_* */ + CINIT(PROXY_SSL_OPTIONS, LONG, 261), + + /* Name of pre proxy to use. */ + CINIT(PRE_PROXY, STRINGPOINT, 262), + + /* The public key in DER form used to validate the proxy public key + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CINIT(PROXY_PINNEDPUBLICKEY, STRINGPOINT, 263), + + /* Path to an abstract Unix domain socket */ + CINIT(ABSTRACT_UNIX_SOCKET, STRINGPOINT, 264), + + /* Suppress proxy CONNECT response headers from user callbacks */ + CINIT(SUPPRESS_CONNECT_HEADERS, LONG, 265), + + /* The request target, instead of extracted from the URL */ + CINIT(REQUEST_TARGET, STRINGPOINT, 266), + + /* bitmask of allowed auth methods for connections to SOCKS5 proxies */ + CINIT(SOCKS5_AUTH, LONG, 267), + + /* Enable/disable SSH compression */ + CINIT(SSH_COMPRESSION, LONG, 268), + + /* Post MIME data. */ + CINIT(MIMEPOST, OBJECTPOINT, 269), + + CURLOPT_LASTENTRY /* the last unused */ +} CURLoption; + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Backwards compatibility with older names */ +/* These are scheduled to disappear by 2011 */ + +/* This was added in version 7.19.1 */ +#define CURLOPT_POST301 CURLOPT_POSTREDIR + +/* These are scheduled to disappear by 2009 */ + +/* The following were added in 7.17.0 */ +#define CURLOPT_SSLKEYPASSWD CURLOPT_KEYPASSWD +#define CURLOPT_FTPAPPEND CURLOPT_APPEND +#define CURLOPT_FTPLISTONLY CURLOPT_DIRLISTONLY +#define CURLOPT_FTP_SSL CURLOPT_USE_SSL + +/* The following were added earlier */ + +#define CURLOPT_SSLCERTPASSWD CURLOPT_KEYPASSWD +#define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL + +#else +/* This is set if CURL_NO_OLDIES is defined at compile-time */ +#undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */ +#endif + + + /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host + name resolves addresses using more than one IP protocol version, this + option might be handy to force libcurl to use a specific IP version. */ +#define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP + versions that your system allows */ +#define CURL_IPRESOLVE_V4 1 /* resolve to IPv4 addresses */ +#define CURL_IPRESOLVE_V6 2 /* resolve to IPv6 addresses */ + + /* three convenient "aliases" that follow the name scheme better */ +#define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER + + /* These enums are for use with the CURLOPT_HTTP_VERSION option. */ +enum { + CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd + like the library to choose the best possible + for us! */ + CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */ + CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */ + CURL_HTTP_VERSION_2_0, /* please use HTTP 2 in the request */ + CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */ + CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, /* please use HTTP 2 without HTTP/1.1 + Upgrade */ + + CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ +}; + +/* Convenience definition simple because the name of the version is HTTP/2 and + not 2.0. The 2_0 version of the enum name was set while the version was + still planned to be 2.0 and we stick to it for compatibility. */ +#define CURL_HTTP_VERSION_2 CURL_HTTP_VERSION_2_0 + +/* + * Public API enums for RTSP requests + */ +enum { + CURL_RTSPREQ_NONE, /* first in list */ + CURL_RTSPREQ_OPTIONS, + CURL_RTSPREQ_DESCRIBE, + CURL_RTSPREQ_ANNOUNCE, + CURL_RTSPREQ_SETUP, + CURL_RTSPREQ_PLAY, + CURL_RTSPREQ_PAUSE, + CURL_RTSPREQ_TEARDOWN, + CURL_RTSPREQ_GET_PARAMETER, + CURL_RTSPREQ_SET_PARAMETER, + CURL_RTSPREQ_RECORD, + CURL_RTSPREQ_RECEIVE, + CURL_RTSPREQ_LAST /* last in list */ +}; + + /* These enums are for use with the CURLOPT_NETRC option. */ +enum CURL_NETRC_OPTION { + CURL_NETRC_IGNORED, /* The .netrc will never be read. + * This is the default. */ + CURL_NETRC_OPTIONAL, /* A user:password in the URL will be preferred + * to one in the .netrc. */ + CURL_NETRC_REQUIRED, /* A user:password in the URL will be ignored. + * Unless one is set programmatically, the .netrc + * will be queried. */ + CURL_NETRC_LAST +}; + +enum { + CURL_SSLVERSION_DEFAULT, + CURL_SSLVERSION_TLSv1, /* TLS 1.x */ + CURL_SSLVERSION_SSLv2, + CURL_SSLVERSION_SSLv3, + CURL_SSLVERSION_TLSv1_0, + CURL_SSLVERSION_TLSv1_1, + CURL_SSLVERSION_TLSv1_2, + CURL_SSLVERSION_TLSv1_3, + + CURL_SSLVERSION_LAST /* never use, keep last */ +}; + +enum { + CURL_SSLVERSION_MAX_NONE = 0, + CURL_SSLVERSION_MAX_DEFAULT = (CURL_SSLVERSION_TLSv1 << 16), + CURL_SSLVERSION_MAX_TLSv1_0 = (CURL_SSLVERSION_TLSv1_0 << 16), + CURL_SSLVERSION_MAX_TLSv1_1 = (CURL_SSLVERSION_TLSv1_1 << 16), + CURL_SSLVERSION_MAX_TLSv1_2 = (CURL_SSLVERSION_TLSv1_2 << 16), + CURL_SSLVERSION_MAX_TLSv1_3 = (CURL_SSLVERSION_TLSv1_3 << 16), + + /* never use, keep last */ + CURL_SSLVERSION_MAX_LAST = (CURL_SSLVERSION_LAST << 16) +}; + +enum CURL_TLSAUTH { + CURL_TLSAUTH_NONE, + CURL_TLSAUTH_SRP, + CURL_TLSAUTH_LAST /* never use, keep last */ +}; + +/* symbols to use with CURLOPT_POSTREDIR. + CURL_REDIR_POST_301, CURL_REDIR_POST_302 and CURL_REDIR_POST_303 + can be bitwise ORed so that CURL_REDIR_POST_301 | CURL_REDIR_POST_302 + | CURL_REDIR_POST_303 == CURL_REDIR_POST_ALL */ + +#define CURL_REDIR_GET_ALL 0 +#define CURL_REDIR_POST_301 1 +#define CURL_REDIR_POST_302 2 +#define CURL_REDIR_POST_303 4 +#define CURL_REDIR_POST_ALL \ + (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303) + +typedef enum { + CURL_TIMECOND_NONE, + + CURL_TIMECOND_IFMODSINCE, + CURL_TIMECOND_IFUNMODSINCE, + CURL_TIMECOND_LASTMOD, + + CURL_TIMECOND_LAST +} curl_TimeCond; + +/* Special size_t value signaling a zero-terminated string. */ +#define CURL_ZERO_TERMINATED ((size_t) -1) + +/* curl_strequal() and curl_strnequal() are subject for removal in a future + release */ +CURL_EXTERN int curl_strequal(const char *s1, const char *s2); +CURL_EXTERN int curl_strnequal(const char *s1, const char *s2, size_t n); + +/* Mime/form handling support. */ +typedef struct curl_mime_s curl_mime; /* Mime context. */ +typedef struct curl_mimepart_s curl_mimepart; /* Mime part context. */ + +/* + * NAME curl_mime_init() + * + * DESCRIPTION + * + * Create a mime context and return its handle. The easy parameter is the + * target handle. + */ +CURL_EXTERN curl_mime *curl_mime_init(CURL *easy); + +/* + * NAME curl_mime_free() + * + * DESCRIPTION + * + * release a mime handle and its substructures. + */ +CURL_EXTERN void curl_mime_free(curl_mime *mime); + +/* + * NAME curl_mime_addpart() + * + * DESCRIPTION + * + * Append a new empty part to the given mime context and return a handle to + * the created part. + */ +CURL_EXTERN curl_mimepart *curl_mime_addpart(curl_mime *mime); + +/* + * NAME curl_mime_name() + * + * DESCRIPTION + * + * Set mime/form part name. + */ +CURL_EXTERN CURLcode curl_mime_name(curl_mimepart *part, const char *name); + +/* + * NAME curl_mime_filename() + * + * DESCRIPTION + * + * Set mime part remote file name. + */ +CURL_EXTERN CURLcode curl_mime_filename(curl_mimepart *part, + const char *filename); + +/* + * NAME curl_mime_type() + * + * DESCRIPTION + * + * Set mime part type. + */ +CURL_EXTERN CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype); + +/* + * NAME curl_mime_encoder() + * + * DESCRIPTION + * + * Set mime data transfer encoder. + */ +CURL_EXTERN CURLcode curl_mime_encoder(curl_mimepart *part, + const char *encoding); + +/* + * NAME curl_mime_data() + * + * DESCRIPTION + * + * Set mime part data source from memory data, + */ +CURL_EXTERN CURLcode curl_mime_data(curl_mimepart *part, + const char *data, size_t datasize); + +/* + * NAME curl_mime_filedata() + * + * DESCRIPTION + * + * Set mime part data source from named file. + */ +CURL_EXTERN CURLcode curl_mime_filedata(curl_mimepart *part, + const char *filename); + +/* + * NAME curl_mime_data_cb() + * + * DESCRIPTION + * + * Set mime part data source from callback function. + */ +CURL_EXTERN CURLcode curl_mime_data_cb(curl_mimepart *part, + curl_off_t datasize, + curl_read_callback readfunc, + curl_seek_callback seekfunc, + curl_free_callback freefunc, + void *arg); + +/* + * NAME curl_mime_subparts() + * + * DESCRIPTION + * + * Set mime part data source from subparts. + */ +CURL_EXTERN CURLcode curl_mime_subparts(curl_mimepart *part, + curl_mime *subparts); +/* + * NAME curl_mime_headers() + * + * DESCRIPTION + * + * Set mime part headers. + */ +CURL_EXTERN CURLcode curl_mime_headers(curl_mimepart *part, + struct curl_slist *headers, + int take_ownership); + +/* Old form API. */ +/* name is uppercase CURLFORM_ */ +#ifdef CFINIT +#undef CFINIT +#endif + +#ifdef CURL_ISOCPP +#define CFINIT(name) CURLFORM_ ## name +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define CFINIT(name) CURLFORM_/**/name +#endif + +typedef enum { + CFINIT(NOTHING), /********* the first one is unused ************/ + + /* */ + CFINIT(COPYNAME), + CFINIT(PTRNAME), + CFINIT(NAMELENGTH), + CFINIT(COPYCONTENTS), + CFINIT(PTRCONTENTS), + CFINIT(CONTENTSLENGTH), + CFINIT(FILECONTENT), + CFINIT(ARRAY), + CFINIT(OBSOLETE), + CFINIT(FILE), + + CFINIT(BUFFER), + CFINIT(BUFFERPTR), + CFINIT(BUFFERLENGTH), + + CFINIT(CONTENTTYPE), + CFINIT(CONTENTHEADER), + CFINIT(FILENAME), + CFINIT(END), + CFINIT(OBSOLETE2), + + CFINIT(STREAM), + CFINIT(CONTENTLEN), /* added in 7.46.0, provide a curl_off_t length */ + + CURLFORM_LASTENTRY /* the last unused */ +} CURLformoption; + +#undef CFINIT /* done */ + +/* structure to be used as parameter for CURLFORM_ARRAY */ +struct curl_forms { + CURLformoption option; + const char *value; +}; + +/* use this for multipart formpost building */ +/* Returns code for curl_formadd() + * + * Returns: + * CURL_FORMADD_OK on success + * CURL_FORMADD_MEMORY if the FormInfo allocation fails + * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form + * CURL_FORMADD_NULL if a null pointer was given for a char + * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed + * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used + * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) + * CURL_FORMADD_MEMORY if a curl_httppost struct cannot be allocated + * CURL_FORMADD_MEMORY if some allocation for string copying failed. + * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array + * + ***************************************************************************/ +typedef enum { + CURL_FORMADD_OK, /* first, no error */ + + CURL_FORMADD_MEMORY, + CURL_FORMADD_OPTION_TWICE, + CURL_FORMADD_NULL, + CURL_FORMADD_UNKNOWN_OPTION, + CURL_FORMADD_INCOMPLETE, + CURL_FORMADD_ILLEGAL_ARRAY, + CURL_FORMADD_DISABLED, /* libcurl was built with this disabled */ + + CURL_FORMADD_LAST /* last */ +} CURLFORMcode; + +/* + * NAME curl_formadd() + * + * DESCRIPTION + * + * Pretty advanced function for building multi-part formposts. Each invoke + * adds one part that together construct a full post. Then use + * CURLOPT_HTTPPOST to send it off to libcurl. + */ +CURL_EXTERN CURLFORMcode curl_formadd(struct curl_httppost **httppost, + struct curl_httppost **last_post, + ...); + +/* + * callback function for curl_formget() + * The void *arg pointer will be the one passed as second argument to + * curl_formget(). + * The character buffer passed to it must not be freed. + * Should return the buffer length passed to it as the argument "len" on + * success. + */ +typedef size_t (*curl_formget_callback)(void *arg, const char *buf, + size_t len); + +/* + * NAME curl_formget() + * + * DESCRIPTION + * + * Serialize a curl_httppost struct built with curl_formadd(). + * Accepts a void pointer as second argument which will be passed to + * the curl_formget_callback function. + * Returns 0 on success. + */ +CURL_EXTERN int curl_formget(struct curl_httppost *form, void *arg, + curl_formget_callback append); +/* + * NAME curl_formfree() + * + * DESCRIPTION + * + * Free a multipart formpost previously built with curl_formadd(). + */ +CURL_EXTERN void curl_formfree(struct curl_httppost *form); + +/* + * NAME curl_getenv() + * + * DESCRIPTION + * + * Returns a malloc()'ed string that MUST be curl_free()ed after usage is + * complete. DEPRECATED - see lib/README.curlx + */ +CURL_EXTERN char *curl_getenv(const char *variable); + +/* + * NAME curl_version() + * + * DESCRIPTION + * + * Returns a static ascii string of the libcurl version. + */ +CURL_EXTERN char *curl_version(void); + +/* + * NAME curl_easy_escape() + * + * DESCRIPTION + * + * Escapes URL strings (converts all letters consider illegal in URLs to their + * %XX versions). This function returns a new allocated string or NULL if an + * error occurred. + */ +CURL_EXTERN char *curl_easy_escape(CURL *handle, + const char *string, + int length); + +/* the previous version: */ +CURL_EXTERN char *curl_escape(const char *string, + int length); + + +/* + * NAME curl_easy_unescape() + * + * DESCRIPTION + * + * Unescapes URL encoding in strings (converts all %XX codes to their 8bit + * versions). This function returns a new allocated string or NULL if an error + * occurred. + * Conversion Note: On non-ASCII platforms the ASCII %XX codes are + * converted into the host encoding. + */ +CURL_EXTERN char *curl_easy_unescape(CURL *handle, + const char *string, + int length, + int *outlength); + +/* the previous version */ +CURL_EXTERN char *curl_unescape(const char *string, + int length); + +/* + * NAME curl_free() + * + * DESCRIPTION + * + * Provided for de-allocation in the same translation unit that did the + * allocation. Added in libcurl 7.10 + */ +CURL_EXTERN void curl_free(void *p); + +/* + * NAME curl_global_init() + * + * DESCRIPTION + * + * curl_global_init() should be invoked exactly once for each application that + * uses libcurl and before any call of other libcurl functions. + * + * This function is not thread-safe! + */ +CURL_EXTERN CURLcode curl_global_init(long flags); + +/* + * NAME curl_global_init_mem() + * + * DESCRIPTION + * + * curl_global_init() or curl_global_init_mem() should be invoked exactly once + * for each application that uses libcurl. This function can be used to + * initialize libcurl and set user defined memory management callback + * functions. Users can implement memory management routines to check for + * memory leaks, check for mis-use of the curl library etc. User registered + * callback routines with be invoked by this library instead of the system + * memory management routines like malloc, free etc. + */ +CURL_EXTERN CURLcode curl_global_init_mem(long flags, + curl_malloc_callback m, + curl_free_callback f, + curl_realloc_callback r, + curl_strdup_callback s, + curl_calloc_callback c); + +/* + * NAME curl_global_cleanup() + * + * DESCRIPTION + * + * curl_global_cleanup() should be invoked exactly once for each application + * that uses libcurl + */ +CURL_EXTERN void curl_global_cleanup(void); + +/* linked-list structure for the CURLOPT_QUOTE option (and other) */ +struct curl_slist { + char *data; + struct curl_slist *next; +}; + +/* + * NAME curl_global_sslset() + * + * DESCRIPTION + * + * When built with multiple SSL backends, curl_global_sslset() allows to + * choose one. This function can only be called once, and it must be called + * *before* curl_global_init(). + * + * The backend can be identified by the id (e.g. CURLSSLBACKEND_OPENSSL). The + * backend can also be specified via the name parameter (passing -1 as id). + * If both id and name are specified, the name will be ignored. If neither id + * nor name are specified, the function will fail with + * CURLSSLSET_UNKNOWN_BACKEND and set the "avail" pointer to the + * NULL-terminated list of available backends. + * + * Upon success, the function returns CURLSSLSET_OK. + * + * If the specified SSL backend is not available, the function returns + * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a NULL-terminated + * list of available SSL backends. + * + * The SSL backend can be set only once. If it has already been set, a + * subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE. + */ + +typedef struct { + curl_sslbackend id; + const char *name; +} curl_ssl_backend; + +typedef enum { + CURLSSLSET_OK = 0, + CURLSSLSET_UNKNOWN_BACKEND, + CURLSSLSET_TOO_LATE, + CURLSSLSET_NO_BACKENDS /* libcurl was built without any SSL support */ +} CURLsslset; + +CURL_EXTERN CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, + const curl_ssl_backend ***avail); + +/* + * NAME curl_slist_append() + * + * DESCRIPTION + * + * Appends a string to a linked list. If no list exists, it will be created + * first. Returns the new list, after appending. + */ +CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *, + const char *); + +/* + * NAME curl_slist_free_all() + * + * DESCRIPTION + * + * free a previously built curl_slist. + */ +CURL_EXTERN void curl_slist_free_all(struct curl_slist *); + +/* + * NAME curl_getdate() + * + * DESCRIPTION + * + * Returns the time, in seconds since 1 Jan 1970 of the time string given in + * the first argument. The time argument in the second parameter is unused + * and should be set to NULL. + */ +CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); + +/* info about the certificate chain, only for OpenSSL builds. Asked + for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ +struct curl_certinfo { + int num_of_certs; /* number of certificates with information */ + struct curl_slist **certinfo; /* for each index in this array, there's a + linked list with textual information in the + format "name: value" */ +}; + +/* Information about the SSL library used and the respective internal SSL + handle, which can be used to obtain further information regarding the + connection. Asked for with CURLINFO_TLS_SSL_PTR or CURLINFO_TLS_SESSION. */ +struct curl_tlssessioninfo { + curl_sslbackend backend; + void *internals; +}; + +#define CURLINFO_STRING 0x100000 +#define CURLINFO_LONG 0x200000 +#define CURLINFO_DOUBLE 0x300000 +#define CURLINFO_SLIST 0x400000 +#define CURLINFO_PTR 0x400000 /* same as SLIST */ +#define CURLINFO_SOCKET 0x500000 +#define CURLINFO_OFF_T 0x600000 +#define CURLINFO_MASK 0x0fffff +#define CURLINFO_TYPEMASK 0xf00000 + +typedef enum { + CURLINFO_NONE, /* first, never use this */ + CURLINFO_EFFECTIVE_URL = CURLINFO_STRING + 1, + CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2, + CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE + 3, + CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE + 4, + CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE + 5, + CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6, + CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE + 7, + CURLINFO_SIZE_UPLOAD_T = CURLINFO_OFF_T + 7, + CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE + 8, + CURLINFO_SIZE_DOWNLOAD_T = CURLINFO_OFF_T + 8, + CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE + 9, + CURLINFO_SPEED_DOWNLOAD_T = CURLINFO_OFF_T + 9, + CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE + 10, + CURLINFO_SPEED_UPLOAD_T = CURLINFO_OFF_T + 10, + CURLINFO_HEADER_SIZE = CURLINFO_LONG + 11, + CURLINFO_REQUEST_SIZE = CURLINFO_LONG + 12, + CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13, + CURLINFO_FILETIME = CURLINFO_LONG + 14, + CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15, + CURLINFO_CONTENT_LENGTH_DOWNLOAD_T = CURLINFO_OFF_T + 15, + CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE + 16, + CURLINFO_CONTENT_LENGTH_UPLOAD_T = CURLINFO_OFF_T + 16, + CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17, + CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18, + CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE + 19, + CURLINFO_REDIRECT_COUNT = CURLINFO_LONG + 20, + CURLINFO_PRIVATE = CURLINFO_STRING + 21, + CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG + 22, + CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG + 23, + CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG + 24, + CURLINFO_OS_ERRNO = CURLINFO_LONG + 25, + CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26, + CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27, + CURLINFO_COOKIELIST = CURLINFO_SLIST + 28, + CURLINFO_LASTSOCKET = CURLINFO_LONG + 29, + CURLINFO_FTP_ENTRY_PATH = CURLINFO_STRING + 30, + CURLINFO_REDIRECT_URL = CURLINFO_STRING + 31, + CURLINFO_PRIMARY_IP = CURLINFO_STRING + 32, + CURLINFO_APPCONNECT_TIME = CURLINFO_DOUBLE + 33, + CURLINFO_CERTINFO = CURLINFO_PTR + 34, + CURLINFO_CONDITION_UNMET = CURLINFO_LONG + 35, + CURLINFO_RTSP_SESSION_ID = CURLINFO_STRING + 36, + CURLINFO_RTSP_CLIENT_CSEQ = CURLINFO_LONG + 37, + CURLINFO_RTSP_SERVER_CSEQ = CURLINFO_LONG + 38, + CURLINFO_RTSP_CSEQ_RECV = CURLINFO_LONG + 39, + CURLINFO_PRIMARY_PORT = CURLINFO_LONG + 40, + CURLINFO_LOCAL_IP = CURLINFO_STRING + 41, + CURLINFO_LOCAL_PORT = CURLINFO_LONG + 42, + CURLINFO_TLS_SESSION = CURLINFO_PTR + 43, + CURLINFO_ACTIVESOCKET = CURLINFO_SOCKET + 44, + CURLINFO_TLS_SSL_PTR = CURLINFO_PTR + 45, + CURLINFO_HTTP_VERSION = CURLINFO_LONG + 46, + CURLINFO_PROXY_SSL_VERIFYRESULT = CURLINFO_LONG + 47, + CURLINFO_PROTOCOL = CURLINFO_LONG + 48, + CURLINFO_SCHEME = CURLINFO_STRING + 49, + /* Fill in new entries below here! */ + + CURLINFO_LASTONE = 49 +} CURLINFO; + +/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as + CURLINFO_HTTP_CODE */ +#define CURLINFO_HTTP_CODE CURLINFO_RESPONSE_CODE + +typedef enum { + CURLCLOSEPOLICY_NONE, /* first, never use this */ + + CURLCLOSEPOLICY_OLDEST, + CURLCLOSEPOLICY_LEAST_RECENTLY_USED, + CURLCLOSEPOLICY_LEAST_TRAFFIC, + CURLCLOSEPOLICY_SLOWEST, + CURLCLOSEPOLICY_CALLBACK, + + CURLCLOSEPOLICY_LAST /* last, never use this */ +} curl_closepolicy; + +#define CURL_GLOBAL_SSL (1<<0) /* no purpose since since 7.57.0 */ +#define CURL_GLOBAL_WIN32 (1<<1) +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) +#define CURL_GLOBAL_NOTHING 0 +#define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL +#define CURL_GLOBAL_ACK_EINTR (1<<2) + + +/***************************************************************************** + * Setup defines, protos etc for the sharing stuff. + */ + +/* Different data locks for a single share */ +typedef enum { + CURL_LOCK_DATA_NONE = 0, + /* CURL_LOCK_DATA_SHARE is used internally to say that + * the locking is just made to change the internal state of the share + * itself. + */ + CURL_LOCK_DATA_SHARE, + CURL_LOCK_DATA_COOKIE, + CURL_LOCK_DATA_DNS, + CURL_LOCK_DATA_SSL_SESSION, + CURL_LOCK_DATA_CONNECT, + CURL_LOCK_DATA_LAST +} curl_lock_data; + +/* Different lock access types */ +typedef enum { + CURL_LOCK_ACCESS_NONE = 0, /* unspecified action */ + CURL_LOCK_ACCESS_SHARED = 1, /* for read perhaps */ + CURL_LOCK_ACCESS_SINGLE = 2, /* for write perhaps */ + CURL_LOCK_ACCESS_LAST /* never use */ +} curl_lock_access; + +typedef void (*curl_lock_function)(CURL *handle, + curl_lock_data data, + curl_lock_access locktype, + void *userptr); +typedef void (*curl_unlock_function)(CURL *handle, + curl_lock_data data, + void *userptr); + + +typedef enum { + CURLSHE_OK, /* all is fine */ + CURLSHE_BAD_OPTION, /* 1 */ + CURLSHE_IN_USE, /* 2 */ + CURLSHE_INVALID, /* 3 */ + CURLSHE_NOMEM, /* 4 out of memory */ + CURLSHE_NOT_BUILT_IN, /* 5 feature not present in lib */ + CURLSHE_LAST /* never use */ +} CURLSHcode; + +typedef enum { + CURLSHOPT_NONE, /* don't use */ + CURLSHOPT_SHARE, /* specify a data type to share */ + CURLSHOPT_UNSHARE, /* specify which data type to stop sharing */ + CURLSHOPT_LOCKFUNC, /* pass in a 'curl_lock_function' pointer */ + CURLSHOPT_UNLOCKFUNC, /* pass in a 'curl_unlock_function' pointer */ + CURLSHOPT_USERDATA, /* pass in a user data pointer used in the lock/unlock + callback functions */ + CURLSHOPT_LAST /* never use */ +} CURLSHoption; + +CURL_EXTERN CURLSH *curl_share_init(void); +CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...); +CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *); + +/**************************************************************************** + * Structures for querying information about the curl library at runtime. + */ + +typedef enum { + CURLVERSION_FIRST, + CURLVERSION_SECOND, + CURLVERSION_THIRD, + CURLVERSION_FOURTH, + CURLVERSION_FIFTH, + CURLVERSION_LAST /* never actually use this */ +} CURLversion; + +/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by + basically all programs ever that want to get version information. It is + meant to be a built-in version number for what kind of struct the caller + expects. If the struct ever changes, we redefine the NOW to another enum + from above. */ +#define CURLVERSION_NOW CURLVERSION_FIFTH + +typedef struct { + CURLversion age; /* age of the returned struct */ + const char *version; /* LIBCURL_VERSION */ + unsigned int version_num; /* LIBCURL_VERSION_NUM */ + const char *host; /* OS/host/cpu/machine when configured */ + int features; /* bitmask, see defines below */ + const char *ssl_version; /* human readable string */ + long ssl_version_num; /* not used anymore, always 0 */ + const char *libz_version; /* human readable string */ + /* protocols is terminated by an entry with a NULL protoname */ + const char * const *protocols; + + /* The fields below this were added in CURLVERSION_SECOND */ + const char *ares; + int ares_num; + + /* This field was added in CURLVERSION_THIRD */ + const char *libidn; + + /* These field were added in CURLVERSION_FOURTH */ + + /* Same as '_libiconv_version' if built with HAVE_ICONV */ + int iconv_ver_num; + + const char *libssh_version; /* human readable string */ + + /* These fields were added in CURLVERSION_FIFTH */ + + unsigned int brotli_ver_num; /* Numeric Brotli version + (MAJOR << 24) | (MINOR << 12) | PATCH */ + const char *brotli_version; /* human readable string. */ + +} curl_version_info_data; + +#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ +#define CURL_VERSION_KERBEROS4 (1<<1) /* Kerberos V4 auth is supported + (deprecated) */ +#define CURL_VERSION_SSL (1<<2) /* SSL options are present */ +#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */ +#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */ +#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth is supported + (deprecated) */ +#define CURL_VERSION_DEBUG (1<<6) /* Built with debug capabilities */ +#define CURL_VERSION_ASYNCHDNS (1<<7) /* Asynchronous DNS resolves */ +#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth is supported */ +#define CURL_VERSION_LARGEFILE (1<<9) /* Supports files larger than 2GB */ +#define CURL_VERSION_IDN (1<<10) /* Internationized Domain Names are + supported */ +#define CURL_VERSION_SSPI (1<<11) /* Built against Windows SSPI */ +#define CURL_VERSION_CONV (1<<12) /* Character conversions supported */ +#define CURL_VERSION_CURLDEBUG (1<<13) /* Debug memory tracking supported */ +#define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ +#define CURL_VERSION_NTLM_WB (1<<15) /* NTLM delegation to winbind helper + is supported */ +#define CURL_VERSION_HTTP2 (1<<16) /* HTTP2 support built-in */ +#define CURL_VERSION_GSSAPI (1<<17) /* Built against a GSS-API library */ +#define CURL_VERSION_KERBEROS5 (1<<18) /* Kerberos V5 auth is supported */ +#define CURL_VERSION_UNIX_SOCKETS (1<<19) /* Unix domain sockets support */ +#define CURL_VERSION_PSL (1<<20) /* Mozilla's Public Suffix List, used + for cookie domain verification */ +#define CURL_VERSION_HTTPS_PROXY (1<<21) /* HTTPS-proxy support built-in */ +#define CURL_VERSION_MULTI_SSL (1<<22) /* Multiple SSL backends available */ +#define CURL_VERSION_BROTLI (1<<23) /* Brotli features are present. */ + + /* + * NAME curl_version_info() + * + * DESCRIPTION + * + * This function returns a pointer to a static copy of the version info + * struct. See above. + */ +CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion); + +/* + * NAME curl_easy_strerror() + * + * DESCRIPTION + * + * The curl_easy_strerror function may be used to turn a CURLcode value + * into the equivalent human readable error string. This is useful + * for printing meaningful error messages. + */ +CURL_EXTERN const char *curl_easy_strerror(CURLcode); + +/* + * NAME curl_share_strerror() + * + * DESCRIPTION + * + * The curl_share_strerror function may be used to turn a CURLSHcode value + * into the equivalent human readable error string. This is useful + * for printing meaningful error messages. + */ +CURL_EXTERN const char *curl_share_strerror(CURLSHcode); + +/* + * NAME curl_easy_pause() + * + * DESCRIPTION + * + * The curl_easy_pause function pauses or unpauses transfers. Select the new + * state by setting the bitmask, use the convenience defines below. + * + */ +CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); + +#define CURLPAUSE_RECV (1<<0) +#define CURLPAUSE_RECV_CONT (0) + +#define CURLPAUSE_SEND (1<<2) +#define CURLPAUSE_SEND_CONT (0) + +#define CURLPAUSE_ALL (CURLPAUSE_RECV|CURLPAUSE_SEND) +#define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT) + +#ifdef __cplusplus +} +#endif + +/* unfortunately, the easy.h and multi.h include files need options and info + stuff before they can be included! */ +#include "easy.h" /* nothing in curl is fun without the easy stuff */ +#include "multi.h" + +/* the typechecker doesn't work in C++ (yet) */ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \ + ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && \ + !defined(__cplusplus) && !defined(CURL_DISABLE_TYPECHECK) +#include "typecheck-gcc.h" +#else +#if defined(__STDC__) && (__STDC__ >= 1) +/* This preprocessor magic that replaces a call with the exact same call is + only done to make sure application authors pass exactly three arguments + to these functions. */ +#define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param) +#define curl_easy_getinfo(handle,info,arg) curl_easy_getinfo(handle,info,arg) +#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) +#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) +#endif /* __STDC__ >= 1 */ +#endif /* gcc >= 4.3 && !__cplusplus */ + +#endif /* __CURL_CURL_H */ diff --git a/ext/curl-7.58.0/x64/include/curl/curlver.h b/ext/curl-7.58.0/x64/include/curl/curlver.h new file mode 100644 index 000000000..6d93cc118 --- /dev/null +++ b/ext/curl-7.58.0/x64/include/curl/curlver.h @@ -0,0 +1,77 @@ +#ifndef __CURL_CURLVER_H +#define __CURL_CURLVER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* This header file contains nothing but libcurl version info, generated by + a script at release-time. This was made its own header file in 7.11.2 */ + +/* This is the global package copyright */ +#define LIBCURL_COPYRIGHT "1996 - 2017 Daniel Stenberg, ." + +/* This is the version number of the libcurl package from which this header + file origins: */ +#define LIBCURL_VERSION "7.58.0" + +/* The numeric version number is also available "in parts" by using these + defines: */ +#define LIBCURL_VERSION_MAJOR 7 +#define LIBCURL_VERSION_MINOR 58 +#define LIBCURL_VERSION_PATCH 0 + +/* This is the numeric version of the libcurl version number, meant for easier + parsing and comparions by programs. The LIBCURL_VERSION_NUM define will + always follow this syntax: + + 0xXXYYZZ + + Where XX, YY and ZZ are the main version, release and patch numbers in + hexadecimal (using 8 bits each). All three numbers are always represented + using two digits. 1.2 would appear as "0x010200" while version 9.11.7 + appears as "0x090b07". + + This 6-digit (24 bits) hexadecimal number does not show pre-release number, + and it is always a greater number in a more recent release. It makes + comparisons with greater than and less than work. + + Note: This define is the full hex number and _does not_ use the + CURL_VERSION_BITS() macro since curl's own configure script greps for it + and needs it to contain the full number. +*/ +#define LIBCURL_VERSION_NUM 0x073a00 + +/* + * This is the date and time when the full source package was created. The + * timestamp is not stored in git, as the timestamp is properly set in the + * tarballs by the maketgz script. + * + * The format of the date follows this template: + * + * "2007-11-23" + */ +#define LIBCURL_TIMESTAMP "2018-01-24" + +#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|z) +#define CURL_AT_LEAST_VERSION(x,y,z) \ + (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z)) + +#endif /* __CURL_CURLVER_H */ diff --git a/ext/curl-7.58.0/x64/include/curl/easy.h b/ext/curl-7.58.0/x64/include/curl/easy.h new file mode 100644 index 000000000..752c5049f --- /dev/null +++ b/ext/curl-7.58.0/x64/include/curl/easy.h @@ -0,0 +1,102 @@ +#ifndef __CURL_EASY_H +#define __CURL_EASY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +CURL_EXTERN CURL *curl_easy_init(void); +CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); +CURL_EXTERN CURLcode curl_easy_perform(CURL *curl); +CURL_EXTERN void curl_easy_cleanup(CURL *curl); + +/* + * NAME curl_easy_getinfo() + * + * DESCRIPTION + * + * Request internal information from the curl session with this function. The + * third argument MUST be a pointer to a long, a pointer to a char * or a + * pointer to a double (as the documentation describes elsewhere). The data + * pointed to will be filled in accordingly and can be relied upon only if the + * function returns CURLE_OK. This function is intended to get used *AFTER* a + * performed transfer, all results from this function are undefined until the + * transfer is completed. + */ +CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); + + +/* + * NAME curl_easy_duphandle() + * + * DESCRIPTION + * + * Creates a new curl session handle with the same options set for the handle + * passed in. Duplicating a handle could only be a matter of cloning data and + * options, internal state info and things like persistent connections cannot + * be transferred. It is useful in multithreaded applications when you can run + * curl_easy_duphandle() for each new thread to avoid a series of identical + * curl_easy_setopt() invokes in every thread. + */ +CURL_EXTERN CURL *curl_easy_duphandle(CURL *curl); + +/* + * NAME curl_easy_reset() + * + * DESCRIPTION + * + * Re-initializes a CURL handle to the default values. This puts back the + * handle to the same state as it was in when it was just created. + * + * It does keep: live connections, the Session ID cache, the DNS cache and the + * cookies. + */ +CURL_EXTERN void curl_easy_reset(CURL *curl); + +/* + * NAME curl_easy_recv() + * + * DESCRIPTION + * + * Receives data from the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, + size_t *n); + +/* + * NAME curl_easy_send() + * + * DESCRIPTION + * + * Sends data over the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer, + size_t buflen, size_t *n); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/curl-7.58.0/x64/include/curl/mprintf.h b/ext/curl-7.58.0/x64/include/curl/mprintf.h new file mode 100644 index 000000000..e20f546e1 --- /dev/null +++ b/ext/curl-7.58.0/x64/include/curl/mprintf.h @@ -0,0 +1,50 @@ +#ifndef __CURL_MPRINTF_H +#define __CURL_MPRINTF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include +#include /* needed for FILE */ +#include "curl.h" /* for CURL_EXTERN */ + +#ifdef __cplusplus +extern "C" { +#endif + +CURL_EXTERN int curl_mprintf(const char *format, ...); +CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...); +CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...); +CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength, + const char *format, ...); +CURL_EXTERN int curl_mvprintf(const char *format, va_list args); +CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args); +CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args); +CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength, + const char *format, va_list args); +CURL_EXTERN char *curl_maprintf(const char *format, ...); +CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); + +#ifdef __cplusplus +} +#endif + +#endif /* __CURL_MPRINTF_H */ diff --git a/ext/curl-7.58.0/x64/include/curl/multi.h b/ext/curl-7.58.0/x64/include/curl/multi.h new file mode 100644 index 000000000..911c91dd1 --- /dev/null +++ b/ext/curl-7.58.0/x64/include/curl/multi.h @@ -0,0 +1,439 @@ +#ifndef __CURL_MULTI_H +#define __CURL_MULTI_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +/* + This is an "external" header file. Don't give away any internals here! + + GOALS + + o Enable a "pull" interface. The application that uses libcurl decides where + and when to ask libcurl to get/send data. + + o Enable multiple simultaneous transfers in the same thread without making it + complicated for the application. + + o Enable the application to select() on its own file descriptors and curl's + file descriptors simultaneous easily. + +*/ + +/* + * This header file should not really need to include "curl.h" since curl.h + * itself includes this file and we expect user applications to do #include + * without the need for especially including multi.h. + * + * For some reason we added this include here at one point, and rather than to + * break existing (wrongly written) libcurl applications, we leave it as-is + * but with this warning attached. + */ +#include "curl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER) +typedef struct Curl_multi CURLM; +#else +typedef void CURLM; +#endif + +typedef enum { + CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or + curl_multi_socket*() soon */ + CURLM_OK, + CURLM_BAD_HANDLE, /* the passed-in handle is not a valid CURLM handle */ + CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */ + CURLM_OUT_OF_MEMORY, /* if you ever get this, you're in deep sh*t */ + CURLM_INTERNAL_ERROR, /* this is a libcurl bug */ + CURLM_BAD_SOCKET, /* the passed in socket argument did not match */ + CURLM_UNKNOWN_OPTION, /* curl_multi_setopt() with unsupported option */ + CURLM_ADDED_ALREADY, /* an easy handle already added to a multi handle was + attempted to get added - again */ + CURLM_LAST +} CURLMcode; + +/* just to make code nicer when using curl_multi_socket() you can now check + for CURLM_CALL_MULTI_SOCKET too in the same style it works for + curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ +#define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM + +/* bitmask bits for CURLMOPT_PIPELINING */ +#define CURLPIPE_NOTHING 0L +#define CURLPIPE_HTTP1 1L +#define CURLPIPE_MULTIPLEX 2L + +typedef enum { + CURLMSG_NONE, /* first, not used */ + CURLMSG_DONE, /* This easy handle has completed. 'result' contains + the CURLcode of the transfer */ + CURLMSG_LAST /* last, not used */ +} CURLMSG; + +struct CURLMsg { + CURLMSG msg; /* what this message means */ + CURL *easy_handle; /* the handle it concerns */ + union { + void *whatever; /* message-specific data */ + CURLcode result; /* return code for transfer */ + } data; +}; +typedef struct CURLMsg CURLMsg; + +/* Based on poll(2) structure and values. + * We don't use pollfd and POLL* constants explicitly + * to cover platforms without poll(). */ +#define CURL_WAIT_POLLIN 0x0001 +#define CURL_WAIT_POLLPRI 0x0002 +#define CURL_WAIT_POLLOUT 0x0004 + +struct curl_waitfd { + curl_socket_t fd; + short events; + short revents; /* not supported yet */ +}; + +/* + * Name: curl_multi_init() + * + * Desc: inititalize multi-style curl usage + * + * Returns: a new CURLM handle to use in all 'curl_multi' functions. + */ +CURL_EXTERN CURLM *curl_multi_init(void); + +/* + * Name: curl_multi_add_handle() + * + * Desc: add a standard curl handle to the multi stack + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_remove_handle() + * + * Desc: removes a curl handle from the multi stack again + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_fdset() + * + * Desc: Ask curl for its fd_set sets. The app can use these to select() or + * poll() on. We want curl_multi_perform() called as soon as one of + * them are ready. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); + +/* + * Name: curl_multi_wait() + * + * Desc: Poll on all fds within a CURLM set as well as any + * additional fds passed to the function. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + int *ret); + + /* + * Name: curl_multi_perform() + * + * Desc: When the app thinks there's data available for curl it calls this + * function to read/write whatever there is right now. This returns + * as soon as the reads and writes are done. This function does not + * require that there actually is data available for reading or that + * data can be written, it can be called just in case. It returns + * the number of handles that still transfer data in the second + * argument's integer-pointer. + * + * Returns: CURLMcode type, general multi error code. *NOTE* that this only + * returns errors etc regarding the whole multi stack. There might + * still have occurred problems on invidual transfers even when this + * returns OK. + */ +CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle, + int *running_handles); + + /* + * Name: curl_multi_cleanup() + * + * Desc: Cleans up and removes a whole multi stack. It does not free or + * touch any individual easy handles in any way. We need to define + * in what state those handles will be if this function is called + * in the middle of a transfer. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); + +/* + * Name: curl_multi_info_read() + * + * Desc: Ask the multi handle if there's any messages/informationals from + * the individual transfers. Messages include informationals such as + * error code from the transfer or just the fact that a transfer is + * completed. More details on these should be written down as well. + * + * Repeated calls to this function will return a new struct each + * time, until a special "end of msgs" struct is returned as a signal + * that there is no more to get at this point. + * + * The data the returned pointer points to will not survive calling + * curl_multi_cleanup(). + * + * The 'CURLMsg' struct is meant to be very simple and only contain + * very basic information. If more involved information is wanted, + * we will provide the particular "transfer handle" in that struct + * and that should/could/would be used in subsequent + * curl_easy_getinfo() calls (or similar). The point being that we + * must never expose complex structs to applications, as then we'll + * undoubtably get backwards compatibility problems in the future. + * + * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out + * of structs. It also writes the number of messages left in the + * queue (after this read) in the integer the second argument points + * to. + */ +CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, + int *msgs_in_queue); + +/* + * Name: curl_multi_strerror() + * + * Desc: The curl_multi_strerror function may be used to turn a CURLMcode + * value into the equivalent human readable error string. This is + * useful for printing meaningful error messages. + * + * Returns: A pointer to a zero-terminated error message. + */ +CURL_EXTERN const char *curl_multi_strerror(CURLMcode); + +/* + * Name: curl_multi_socket() and + * curl_multi_socket_all() + * + * Desc: An alternative version of curl_multi_perform() that allows the + * application to pass in one of the file descriptors that have been + * detected to have "action" on them and let libcurl perform. + * See man page for details. + */ +#define CURL_POLL_NONE 0 +#define CURL_POLL_IN 1 +#define CURL_POLL_OUT 2 +#define CURL_POLL_INOUT 3 +#define CURL_POLL_REMOVE 4 + +#define CURL_SOCKET_TIMEOUT CURL_SOCKET_BAD + +#define CURL_CSELECT_IN 0x01 +#define CURL_CSELECT_OUT 0x02 +#define CURL_CSELECT_ERR 0x04 + +typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */ + curl_socket_t s, /* socket */ + int what, /* see above */ + void *userp, /* private callback + pointer */ + void *socketp); /* private socket + pointer */ +/* + * Name: curl_multi_timer_callback + * + * Desc: Called by libcurl whenever the library detects a change in the + * maximum number of milliseconds the app is allowed to wait before + * curl_multi_socket() or curl_multi_perform() must be called + * (to allow libcurl's timed events to take place). + * + * Returns: The callback should return zero. + */ +typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */ + long timeout_ms, /* see above */ + void *userp); /* private callback + pointer */ + +CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, + int *running_handles); + +CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle, + curl_socket_t s, + int ev_bitmask, + int *running_handles); + +CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle, + int *running_handles); + +#ifndef CURL_ALLOW_OLD_MULTI_SOCKET +/* This macro below was added in 7.16.3 to push users who recompile to use + the new curl_multi_socket_action() instead of the old curl_multi_socket() +*/ +#define curl_multi_socket(x,y,z) curl_multi_socket_action(x,y,0,z) +#endif + +/* + * Name: curl_multi_timeout() + * + * Desc: Returns the maximum number of milliseconds the app is allowed to + * wait before curl_multi_socket() or curl_multi_perform() must be + * called (to allow libcurl's timed events to take place). + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle, + long *milliseconds); + +#undef CINIT /* re-using the same name as in curl.h */ + +#ifdef CURL_ISOCPP +#define CINIT(name,type,num) CURLMOPT_ ## name = CURLOPTTYPE_ ## type + num +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define LONG CURLOPTTYPE_LONG +#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT +#define OFF_T CURLOPTTYPE_OFF_T +#define CINIT(name,type,number) CURLMOPT_/**/name = type + number +#endif + +typedef enum { + /* This is the socket callback function pointer */ + CINIT(SOCKETFUNCTION, FUNCTIONPOINT, 1), + + /* This is the argument passed to the socket callback */ + CINIT(SOCKETDATA, OBJECTPOINT, 2), + + /* set to 1 to enable pipelining for this multi handle */ + CINIT(PIPELINING, LONG, 3), + + /* This is the timer callback function pointer */ + CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4), + + /* This is the argument passed to the timer callback */ + CINIT(TIMERDATA, OBJECTPOINT, 5), + + /* maximum number of entries in the connection cache */ + CINIT(MAXCONNECTS, LONG, 6), + + /* maximum number of (pipelining) connections to one host */ + CINIT(MAX_HOST_CONNECTIONS, LONG, 7), + + /* maximum number of requests in a pipeline */ + CINIT(MAX_PIPELINE_LENGTH, LONG, 8), + + /* a connection with a content-length longer than this + will not be considered for pipelining */ + CINIT(CONTENT_LENGTH_PENALTY_SIZE, OFF_T, 9), + + /* a connection with a chunk length longer than this + will not be considered for pipelining */ + CINIT(CHUNK_LENGTH_PENALTY_SIZE, OFF_T, 10), + + /* a list of site names(+port) that are blacklisted from + pipelining */ + CINIT(PIPELINING_SITE_BL, OBJECTPOINT, 11), + + /* a list of server types that are blacklisted from + pipelining */ + CINIT(PIPELINING_SERVER_BL, OBJECTPOINT, 12), + + /* maximum number of open connections in total */ + CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13), + + /* This is the server push callback function pointer */ + CINIT(PUSHFUNCTION, FUNCTIONPOINT, 14), + + /* This is the argument passed to the server push callback */ + CINIT(PUSHDATA, OBJECTPOINT, 15), + + CURLMOPT_LASTENTRY /* the last unused */ +} CURLMoption; + + +/* + * Name: curl_multi_setopt() + * + * Desc: Sets options for the multi handle. + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, + CURLMoption option, ...); + + +/* + * Name: curl_multi_assign() + * + * Desc: This function sets an association in the multi handle between the + * given socket and a private pointer of the application. This is + * (only) useful for curl_multi_socket uses. + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, + curl_socket_t sockfd, void *sockp); + + +/* + * Name: curl_push_callback + * + * Desc: This callback gets called when a new stream is being pushed by the + * server. It approves or denies the new stream. + * + * Returns: CURL_PUSH_OK or CURL_PUSH_DENY. + */ +#define CURL_PUSH_OK 0 +#define CURL_PUSH_DENY 1 + +struct curl_pushheaders; /* forward declaration only */ + +CURL_EXTERN char *curl_pushheader_bynum(struct curl_pushheaders *h, + size_t num); +CURL_EXTERN char *curl_pushheader_byname(struct curl_pushheaders *h, + const char *name); + +typedef int (*curl_push_callback)(CURL *parent, + CURL *easy, + size_t num_headers, + struct curl_pushheaders *headers, + void *userp); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif diff --git a/ext/curl-7.58.0/x64/include/curl/stdcheaders.h b/ext/curl-7.58.0/x64/include/curl/stdcheaders.h new file mode 100644 index 000000000..027b6f421 --- /dev/null +++ b/ext/curl-7.58.0/x64/include/curl/stdcheaders.h @@ -0,0 +1,33 @@ +#ifndef __STDC_HEADERS_H +#define __STDC_HEADERS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include + +size_t fread(void *, size_t, size_t, FILE *); +size_t fwrite(const void *, size_t, size_t, FILE *); + +int strcasecmp(const char *, const char *); +int strncasecmp(const char *, const char *, size_t); + +#endif /* __STDC_HEADERS_H */ diff --git a/ext/curl-7.58.0/x64/include/curl/system.h b/ext/curl-7.58.0/x64/include/curl/system.h new file mode 100644 index 000000000..07bbd9ca9 --- /dev/null +++ b/ext/curl-7.58.0/x64/include/curl/system.h @@ -0,0 +1,473 @@ +#ifndef __CURL_SYSTEM_H +#define __CURL_SYSTEM_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* + * Try to keep one section per platform, compiler and architecture, otherwise, + * if an existing section is reused for a different one and later on the + * original is adjusted, probably the piggybacking one can be adversely + * changed. + * + * In order to differentiate between platforms/compilers/architectures use + * only compiler built in predefined preprocessor symbols. + * + * curl_off_t + * ---------- + * + * For any given platform/compiler curl_off_t must be typedef'ed to a 64-bit + * wide signed integral data type. The width of this data type must remain + * constant and independent of any possible large file support settings. + * + * As an exception to the above, curl_off_t shall be typedef'ed to a 32-bit + * wide signed integral data type if there is no 64-bit type. + * + * As a general rule, curl_off_t shall not be mapped to off_t. This rule shall + * only be violated if off_t is the only 64-bit data type available and the + * size of off_t is independent of large file support settings. Keep your + * build on the safe side avoiding an off_t gating. If you have a 64-bit + * off_t then take for sure that another 64-bit data type exists, dig deeper + * and you will find it. + * + */ + +#if defined(__DJGPP__) || defined(__GO32__) +# if defined(__DJGPP__) && (__DJGPP__ > 1) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__SALFORDC__) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__BORLANDC__) +# if (__BORLANDC__ < 0x520) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__TURBOC__) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__WATCOMC__) +# if defined(__386__) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__POCC__) +# if (__POCC__ < 280) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# elif defined(_MSC_VER) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__LCC__) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__SYMBIAN32__) +# if defined(__EABI__) /* Treat all ARM compilers equally */ +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__CW32__) +# pragma longlong on +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__VC32__) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int + +#elif defined(__MWERKS__) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(_WIN32_WCE) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__MINGW32__) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_WS2TCPIP_H 1 + +#elif defined(__VMS) +# if defined(__VAX) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int + +#elif defined(__OS400__) +# if defined(__ILEC400__) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(__MVS__) +# if defined(__IBMC__) || defined(__IBMCPP__) +# if defined(_ILP32) +# elif defined(_LP64) +# endif +# if defined(_LONG_LONG) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(__370__) +# if defined(__IBMC__) || defined(__IBMCPP__) +# if defined(_ILP32) +# elif defined(_LP64) +# endif +# if defined(_LONG_LONG) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(TPF) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__TINYC__) /* also known as tcc */ + +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#elif defined(__SUNPRO_C) /* Oracle Solaris Studio */ +# if !defined(__LP64) && (defined(__ILP32) || \ + defined(__i386) || defined(__sparcv8)) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__LP64) || \ + defined(__amd64) || defined(__sparcv9) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +/* ===================================== */ +/* KEEP MSVC THE PENULTIMATE ENTRY */ +/* ===================================== */ + +#elif defined(_MSC_VER) +# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +/* ===================================== */ +/* KEEP GENERIC GCC THE LAST ENTRY */ +/* ===================================== */ + +#elif defined(__GNUC__) +# if !defined(__LP64__) && \ + (defined(__ILP32__) || defined(__i386__) || defined(__hppa__) || \ + defined(__ppc__) || defined(__powerpc__) || defined(__arm__) || \ + defined(__sparc__) || defined(__mips__) || defined(__sh__) || \ + defined(__XTENSA__) || \ + (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4) || \ + (defined(__LONG_MAX__) && __LONG_MAX__ == 2147483647L)) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__LP64__) || \ + defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) || \ + (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 8) || \ + (defined(__LONG_MAX__) && __LONG_MAX__ == 9223372036854775807L) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#else +/* generic "safe guess" on old 32 bit style */ +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +#endif + +#ifdef _AIX +/* AIX needs */ +#define CURL_PULL_SYS_POLL_H +#endif + + +/* CURL_PULL_WS2TCPIP_H is defined above when inclusion of header file */ +/* ws2tcpip.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_WS2TCPIP_H +# include +# include +# include +#endif + +/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file */ +/* sys/types.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_TYPES_H +# include +#endif + +/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file */ +/* sys/socket.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_SOCKET_H +# include +#endif + +/* CURL_PULL_SYS_POLL_H is defined above when inclusion of header file */ +/* sys/poll.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_POLL_H +# include +#endif + +/* Data type definition of curl_socklen_t. */ +#ifdef CURL_TYPEOF_CURL_SOCKLEN_T + typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t; +#endif + +/* Data type definition of curl_off_t. */ + +#ifdef CURL_TYPEOF_CURL_OFF_T + typedef CURL_TYPEOF_CURL_OFF_T curl_off_t; +#endif + +/* + * CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow + * these to be visible and exported by the external libcurl interface API, + * while also making them visible to the library internals, simply including + * curl_setup.h, without actually needing to include curl.h internally. + * If some day this section would grow big enough, all this should be moved + * to its own header file. + */ + +/* + * Figure out if we can use the ## preprocessor operator, which is supported + * by ISO/ANSI C and C++. Some compilers support it without setting __STDC__ + * or __cplusplus so we need to carefully check for them too. + */ + +#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \ + defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \ + defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \ + defined(__ILEC400__) + /* This compiler is believed to have an ISO compatible preprocessor */ +#define CURL_ISOCPP +#else + /* This compiler is believed NOT to have an ISO compatible preprocessor */ +#undef CURL_ISOCPP +#endif + +/* + * Macros for minimum-width signed and unsigned curl_off_t integer constants. + */ + +#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551) +# define __CURL_OFF_T_C_HLPR2(x) x +# define __CURL_OFF_T_C_HLPR1(x) __CURL_OFF_T_C_HLPR2(x) +# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \ + __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T) +# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \ + __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU) +#else +# ifdef CURL_ISOCPP +# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix +# else +# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix +# endif +# define __CURL_OFF_T_C_HLPR1(Val,Suffix) __CURL_OFF_T_C_HLPR2(Val,Suffix) +# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T) +# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU) +#endif + +#endif /* __CURL_SYSTEM_H */ diff --git a/ext/curl-7.58.0/x64/include/curl/typecheck-gcc.h b/ext/curl-7.58.0/x64/include/curl/typecheck-gcc.h new file mode 100644 index 000000000..10c74c764 --- /dev/null +++ b/ext/curl-7.58.0/x64/include/curl/typecheck-gcc.h @@ -0,0 +1,683 @@ +#ifndef __CURL_TYPECHECK_GCC_H +#define __CURL_TYPECHECK_GCC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* wraps curl_easy_setopt() with typechecking */ + +/* To add a new kind of warning, add an + * if(_curl_is_sometype_option(_curl_opt)) + * if(!_curl_is_sometype(value)) + * _curl_easy_setopt_err_sometype(); + * block and define _curl_is_sometype_option, _curl_is_sometype and + * _curl_easy_setopt_err_sometype below + * + * NOTE: We use two nested 'if' statements here instead of the && operator, in + * order to work around gcc bug #32061. It affects only gcc 4.3.x/4.4.x + * when compiling with -Wlogical-op. + * + * To add an option that uses the same type as an existing option, you'll just + * need to extend the appropriate _curl_*_option macro + */ +#define curl_easy_setopt(handle, option, value) \ +__extension__ ({ \ + __typeof__(option) _curl_opt = option; \ + if(__builtin_constant_p(_curl_opt)) { \ + if(_curl_is_long_option(_curl_opt)) \ + if(!_curl_is_long(value)) \ + _curl_easy_setopt_err_long(); \ + if(_curl_is_off_t_option(_curl_opt)) \ + if(!_curl_is_off_t(value)) \ + _curl_easy_setopt_err_curl_off_t(); \ + if(_curl_is_string_option(_curl_opt)) \ + if(!_curl_is_string(value)) \ + _curl_easy_setopt_err_string(); \ + if(_curl_is_write_cb_option(_curl_opt)) \ + if(!_curl_is_write_cb(value)) \ + _curl_easy_setopt_err_write_callback(); \ + if((_curl_opt) == CURLOPT_READFUNCTION) \ + if(!_curl_is_read_cb(value)) \ + _curl_easy_setopt_err_read_cb(); \ + if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \ + if(!_curl_is_ioctl_cb(value)) \ + _curl_easy_setopt_err_ioctl_cb(); \ + if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \ + if(!_curl_is_sockopt_cb(value)) \ + _curl_easy_setopt_err_sockopt_cb(); \ + if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \ + if(!_curl_is_opensocket_cb(value)) \ + _curl_easy_setopt_err_opensocket_cb(); \ + if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \ + if(!_curl_is_progress_cb(value)) \ + _curl_easy_setopt_err_progress_cb(); \ + if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \ + if(!_curl_is_debug_cb(value)) \ + _curl_easy_setopt_err_debug_cb(); \ + if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \ + if(!_curl_is_ssl_ctx_cb(value)) \ + _curl_easy_setopt_err_ssl_ctx_cb(); \ + if(_curl_is_conv_cb_option(_curl_opt)) \ + if(!_curl_is_conv_cb(value)) \ + _curl_easy_setopt_err_conv_cb(); \ + if((_curl_opt) == CURLOPT_SEEKFUNCTION) \ + if(!_curl_is_seek_cb(value)) \ + _curl_easy_setopt_err_seek_cb(); \ + if(_curl_is_cb_data_option(_curl_opt)) \ + if(!_curl_is_cb_data(value)) \ + _curl_easy_setopt_err_cb_data(); \ + if((_curl_opt) == CURLOPT_ERRORBUFFER) \ + if(!_curl_is_error_buffer(value)) \ + _curl_easy_setopt_err_error_buffer(); \ + if((_curl_opt) == CURLOPT_STDERR) \ + if(!_curl_is_FILE(value)) \ + _curl_easy_setopt_err_FILE(); \ + if(_curl_is_postfields_option(_curl_opt)) \ + if(!_curl_is_postfields(value)) \ + _curl_easy_setopt_err_postfields(); \ + if((_curl_opt) == CURLOPT_HTTPPOST) \ + if(!_curl_is_arr((value), struct curl_httppost)) \ + _curl_easy_setopt_err_curl_httpost(); \ + if((_curl_opt) == CURLOPT_MIMEPOST) \ + if(!_curl_is_ptr((value), curl_mime)) \ + _curl_easy_setopt_err_curl_mimepost(); \ + if(_curl_is_slist_option(_curl_opt)) \ + if(!_curl_is_arr((value), struct curl_slist)) \ + _curl_easy_setopt_err_curl_slist(); \ + if((_curl_opt) == CURLOPT_SHARE) \ + if(!_curl_is_ptr((value), CURLSH)) \ + _curl_easy_setopt_err_CURLSH(); \ + } \ + curl_easy_setopt(handle, _curl_opt, value); \ +}) + +/* wraps curl_easy_getinfo() with typechecking */ +/* FIXME: don't allow const pointers */ +#define curl_easy_getinfo(handle, info, arg) \ +__extension__ ({ \ + __typeof__(info) _curl_info = info; \ + if(__builtin_constant_p(_curl_info)) { \ + if(_curl_is_string_info(_curl_info)) \ + if(!_curl_is_arr((arg), char *)) \ + _curl_easy_getinfo_err_string(); \ + if(_curl_is_long_info(_curl_info)) \ + if(!_curl_is_arr((arg), long)) \ + _curl_easy_getinfo_err_long(); \ + if(_curl_is_double_info(_curl_info)) \ + if(!_curl_is_arr((arg), double)) \ + _curl_easy_getinfo_err_double(); \ + if(_curl_is_slist_info(_curl_info)) \ + if(!_curl_is_arr((arg), struct curl_slist *)) \ + _curl_easy_getinfo_err_curl_slist(); \ + if(_curl_is_tlssessioninfo_info(_curl_info)) \ + if(!_curl_is_arr((arg), struct curl_tlssessioninfo *)) \ + _curl_easy_getinfo_err_curl_tlssesssioninfo(); \ + if(_curl_is_certinfo_info(_curl_info)) \ + if(!_curl_is_arr((arg), struct curl_certinfo *)) \ + _curl_easy_getinfo_err_curl_certinfo(); \ + if(_curl_is_socket_info(_curl_info)) \ + if(!_curl_is_arr((arg), curl_socket_t)) \ + _curl_easy_getinfo_err_curl_socket(); \ + if(_curl_is_off_t_info(_curl_info)) \ + if(!_curl_is_arr((arg), curl_off_t)) \ + _curl_easy_getinfo_err_curl_off_t(); \ + } \ + curl_easy_getinfo(handle, _curl_info, arg); \ +}) + +/* TODO: typechecking for curl_share_setopt() and curl_multi_setopt(), + * for now just make sure that the functions are called with three + * arguments + */ +#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) +#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) + + +/* the actual warnings, triggered by calling the _curl_easy_setopt_err* + * functions */ + +/* To define a new warning, use _CURL_WARNING(identifier, "message") */ +#define _CURL_WARNING(id, message) \ + static void __attribute__((__warning__(message))) \ + __attribute__((__unused__)) __attribute__((__noinline__)) \ + id(void) { __asm__(""); } + +_CURL_WARNING(_curl_easy_setopt_err_long, + "curl_easy_setopt expects a long argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_off_t, + "curl_easy_setopt expects a curl_off_t argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_string, + "curl_easy_setopt expects a " + "string ('char *' or char[]) argument for this option" + ) +_CURL_WARNING(_curl_easy_setopt_err_write_callback, + "curl_easy_setopt expects a curl_write_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_read_cb, + "curl_easy_setopt expects a curl_read_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_ioctl_cb, + "curl_easy_setopt expects a curl_ioctl_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_sockopt_cb, + "curl_easy_setopt expects a curl_sockopt_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_opensocket_cb, + "curl_easy_setopt expects a " + "curl_opensocket_callback argument for this option" + ) +_CURL_WARNING(_curl_easy_setopt_err_progress_cb, + "curl_easy_setopt expects a curl_progress_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_debug_cb, + "curl_easy_setopt expects a curl_debug_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_ssl_ctx_cb, + "curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_conv_cb, + "curl_easy_setopt expects a curl_conv_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_seek_cb, + "curl_easy_setopt expects a curl_seek_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_cb_data, + "curl_easy_setopt expects a " + "private data pointer as argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_error_buffer, + "curl_easy_setopt expects a " + "char buffer of CURL_ERROR_SIZE as argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_FILE, + "curl_easy_setopt expects a 'FILE *' argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_postfields, + "curl_easy_setopt expects a 'void *' or 'char *' argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_httpost, + "curl_easy_setopt expects a 'struct curl_httppost *' " + "argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_mimepost, + "curl_easy_setopt expects a 'curl_mime *' " + "argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_slist, + "curl_easy_setopt expects a 'struct curl_slist *' argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_CURLSH, + "curl_easy_setopt expects a CURLSH* argument for this option") + +_CURL_WARNING(_curl_easy_getinfo_err_string, + "curl_easy_getinfo expects a pointer to 'char *' for this info") +_CURL_WARNING(_curl_easy_getinfo_err_long, + "curl_easy_getinfo expects a pointer to long for this info") +_CURL_WARNING(_curl_easy_getinfo_err_double, + "curl_easy_getinfo expects a pointer to double for this info") +_CURL_WARNING(_curl_easy_getinfo_err_curl_slist, + "curl_easy_getinfo expects a pointer to 'struct curl_slist *' for this info") +_CURL_WARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo, + "curl_easy_getinfo expects a pointer to " + "'struct curl_tlssessioninfo *' for this info") +_CURL_WARNING(_curl_easy_getinfo_err_curl_certinfo, + "curl_easy_getinfo expects a pointer to " + "'struct curl_certinfo *' for this info") +_CURL_WARNING(_curl_easy_getinfo_err_curl_socket, + "curl_easy_getinfo expects a pointer to curl_socket_t for this info") +_CURL_WARNING(_curl_easy_getinfo_err_curl_off_t, + "curl_easy_getinfo expects a pointer to curl_off_t for this info") + +/* groups of curl_easy_setops options that take the same type of argument */ + +/* To add a new option to one of the groups, just add + * (option) == CURLOPT_SOMETHING + * to the or-expression. If the option takes a long or curl_off_t, you don't + * have to do anything + */ + +/* evaluates to true if option takes a long argument */ +#define _curl_is_long_option(option) \ + (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT) + +#define _curl_is_off_t_option(option) \ + ((option) > CURLOPTTYPE_OFF_T) + +/* evaluates to true if option takes a char* argument */ +#define _curl_is_string_option(option) \ + ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET || \ + (option) == CURLOPT_ACCEPT_ENCODING || \ + (option) == CURLOPT_CAINFO || \ + (option) == CURLOPT_CAPATH || \ + (option) == CURLOPT_COOKIE || \ + (option) == CURLOPT_COOKIEFILE || \ + (option) == CURLOPT_COOKIEJAR || \ + (option) == CURLOPT_COOKIELIST || \ + (option) == CURLOPT_CRLFILE || \ + (option) == CURLOPT_CUSTOMREQUEST || \ + (option) == CURLOPT_DEFAULT_PROTOCOL || \ + (option) == CURLOPT_DNS_INTERFACE || \ + (option) == CURLOPT_DNS_LOCAL_IP4 || \ + (option) == CURLOPT_DNS_LOCAL_IP6 || \ + (option) == CURLOPT_DNS_SERVERS || \ + (option) == CURLOPT_EGDSOCKET || \ + (option) == CURLOPT_FTPPORT || \ + (option) == CURLOPT_FTP_ACCOUNT || \ + (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ + (option) == CURLOPT_INTERFACE || \ + (option) == CURLOPT_ISSUERCERT || \ + (option) == CURLOPT_KEYPASSWD || \ + (option) == CURLOPT_KRBLEVEL || \ + (option) == CURLOPT_LOGIN_OPTIONS || \ + (option) == CURLOPT_MAIL_AUTH || \ + (option) == CURLOPT_MAIL_FROM || \ + (option) == CURLOPT_NETRC_FILE || \ + (option) == CURLOPT_NOPROXY || \ + (option) == CURLOPT_PASSWORD || \ + (option) == CURLOPT_PINNEDPUBLICKEY || \ + (option) == CURLOPT_PRE_PROXY || \ + (option) == CURLOPT_PROXY || \ + (option) == CURLOPT_PROXYPASSWORD || \ + (option) == CURLOPT_PROXYUSERNAME || \ + (option) == CURLOPT_PROXYUSERPWD || \ + (option) == CURLOPT_PROXY_CAINFO || \ + (option) == CURLOPT_PROXY_CAPATH || \ + (option) == CURLOPT_PROXY_CRLFILE || \ + (option) == CURLOPT_PROXY_KEYPASSWD || \ + (option) == CURLOPT_PROXY_PINNEDPUBLICKEY || \ + (option) == CURLOPT_PROXY_SERVICE_NAME || \ + (option) == CURLOPT_PROXY_SSLCERT || \ + (option) == CURLOPT_PROXY_SSLCERTTYPE || \ + (option) == CURLOPT_PROXY_SSLKEY || \ + (option) == CURLOPT_PROXY_SSLKEYTYPE || \ + (option) == CURLOPT_PROXY_SSL_CIPHER_LIST || \ + (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD || \ + (option) == CURLOPT_PROXY_TLSAUTH_USERNAME || \ + (option) == CURLOPT_PROXY_TLSAUTH_TYPE || \ + (option) == CURLOPT_RANDOM_FILE || \ + (option) == CURLOPT_RANGE || \ + (option) == CURLOPT_REFERER || \ + (option) == CURLOPT_RTSP_SESSION_ID || \ + (option) == CURLOPT_RTSP_STREAM_URI || \ + (option) == CURLOPT_RTSP_TRANSPORT || \ + (option) == CURLOPT_SERVICE_NAME || \ + (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \ + (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \ + (option) == CURLOPT_SSH_KNOWNHOSTS || \ + (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \ + (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \ + (option) == CURLOPT_SSLCERT || \ + (option) == CURLOPT_SSLCERTTYPE || \ + (option) == CURLOPT_SSLENGINE || \ + (option) == CURLOPT_SSLKEY || \ + (option) == CURLOPT_SSLKEYTYPE || \ + (option) == CURLOPT_SSL_CIPHER_LIST || \ + (option) == CURLOPT_TLSAUTH_PASSWORD || \ + (option) == CURLOPT_TLSAUTH_TYPE || \ + (option) == CURLOPT_TLSAUTH_USERNAME || \ + (option) == CURLOPT_UNIX_SOCKET_PATH || \ + (option) == CURLOPT_URL || \ + (option) == CURLOPT_USERAGENT || \ + (option) == CURLOPT_USERNAME || \ + (option) == CURLOPT_USERPWD || \ + (option) == CURLOPT_XOAUTH2_BEARER || \ + 0) + +/* evaluates to true if option takes a curl_write_callback argument */ +#define _curl_is_write_cb_option(option) \ + ((option) == CURLOPT_HEADERFUNCTION || \ + (option) == CURLOPT_WRITEFUNCTION) + +/* evaluates to true if option takes a curl_conv_callback argument */ +#define _curl_is_conv_cb_option(option) \ + ((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION || \ + (option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION || \ + (option) == CURLOPT_CONV_FROM_UTF8_FUNCTION) + +/* evaluates to true if option takes a data argument to pass to a callback */ +#define _curl_is_cb_data_option(option) \ + ((option) == CURLOPT_CHUNK_DATA || \ + (option) == CURLOPT_CLOSESOCKETDATA || \ + (option) == CURLOPT_DEBUGDATA || \ + (option) == CURLOPT_FNMATCH_DATA || \ + (option) == CURLOPT_HEADERDATA || \ + (option) == CURLOPT_INTERLEAVEDATA || \ + (option) == CURLOPT_IOCTLDATA || \ + (option) == CURLOPT_OPENSOCKETDATA || \ + (option) == CURLOPT_PRIVATE || \ + (option) == CURLOPT_PROGRESSDATA || \ + (option) == CURLOPT_READDATA || \ + (option) == CURLOPT_SEEKDATA || \ + (option) == CURLOPT_SOCKOPTDATA || \ + (option) == CURLOPT_SSH_KEYDATA || \ + (option) == CURLOPT_SSL_CTX_DATA || \ + (option) == CURLOPT_WRITEDATA || \ + 0) + +/* evaluates to true if option takes a POST data argument (void* or char*) */ +#define _curl_is_postfields_option(option) \ + ((option) == CURLOPT_POSTFIELDS || \ + (option) == CURLOPT_COPYPOSTFIELDS || \ + 0) + +/* evaluates to true if option takes a struct curl_slist * argument */ +#define _curl_is_slist_option(option) \ + ((option) == CURLOPT_HTTP200ALIASES || \ + (option) == CURLOPT_HTTPHEADER || \ + (option) == CURLOPT_MAIL_RCPT || \ + (option) == CURLOPT_POSTQUOTE || \ + (option) == CURLOPT_PREQUOTE || \ + (option) == CURLOPT_PROXYHEADER || \ + (option) == CURLOPT_QUOTE || \ + (option) == CURLOPT_RESOLVE || \ + (option) == CURLOPT_TELNETOPTIONS || \ + 0) + +/* groups of curl_easy_getinfo infos that take the same type of argument */ + +/* evaluates to true if info expects a pointer to char * argument */ +#define _curl_is_string_info(info) \ + (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG) + +/* evaluates to true if info expects a pointer to long argument */ +#define _curl_is_long_info(info) \ + (CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE) + +/* evaluates to true if info expects a pointer to double argument */ +#define _curl_is_double_info(info) \ + (CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST) + +/* true if info expects a pointer to struct curl_slist * argument */ +#define _curl_is_slist_info(info) \ + (((info) == CURLINFO_SSL_ENGINES) || ((info) == CURLINFO_COOKIELIST)) + +/* true if info expects a pointer to struct curl_tlssessioninfo * argument */ +#define _curl_is_tlssessioninfo_info(info) \ + (((info) == CURLINFO_TLS_SSL_PTR) || ((info) == CURLINFO_TLS_SESSION)) + +/* true if info expects a pointer to struct curl_certinfo * argument */ +#define _curl_is_certinfo_info(info) ((info) == CURLINFO_CERTINFO) + +/* true if info expects a pointer to struct curl_socket_t argument */ +#define _curl_is_socket_info(info) \ + (CURLINFO_SOCKET < (info) && (info) < CURLINFO_OFF_T) + +/* true if info expects a pointer to curl_off_t argument */ +#define _curl_is_off_t_info(info) \ + (CURLINFO_OFF_T < (info)) + + +/* typecheck helpers -- check whether given expression has requested type*/ + +/* For pointers, you can use the _curl_is_ptr/_curl_is_arr macros, + * otherwise define a new macro. Search for __builtin_types_compatible_p + * in the GCC manual. + * NOTE: these macros MUST NOT EVALUATE their arguments! The argument is + * the actual expression passed to the curl_easy_setopt macro. This + * means that you can only apply the sizeof and __typeof__ operators, no + * == or whatsoever. + */ + +/* XXX: should evaluate to true iff expr is a pointer */ +#define _curl_is_any_ptr(expr) \ + (sizeof(expr) == sizeof(void *)) + +/* evaluates to true if expr is NULL */ +/* XXX: must not evaluate expr, so this check is not accurate */ +#define _curl_is_NULL(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), __typeof__(NULL))) + +/* evaluates to true if expr is type*, const type* or NULL */ +#define _curl_is_ptr(expr, type) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), type *) || \ + __builtin_types_compatible_p(__typeof__(expr), const type *)) + +/* evaluates to true if expr is one of type[], type*, NULL or const type* */ +#define _curl_is_arr(expr, type) \ + (_curl_is_ptr((expr), type) || \ + __builtin_types_compatible_p(__typeof__(expr), type [])) + +/* evaluates to true if expr is a string */ +#define _curl_is_string(expr) \ + (_curl_is_arr((expr), char) || \ + _curl_is_arr((expr), signed char) || \ + _curl_is_arr((expr), unsigned char)) + +/* evaluates to true if expr is a long (no matter the signedness) + * XXX: for now, int is also accepted (and therefore short and char, which + * are promoted to int when passed to a variadic function) */ +#define _curl_is_long(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), long) || \ + __builtin_types_compatible_p(__typeof__(expr), signed long) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned long) || \ + __builtin_types_compatible_p(__typeof__(expr), int) || \ + __builtin_types_compatible_p(__typeof__(expr), signed int) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned int) || \ + __builtin_types_compatible_p(__typeof__(expr), short) || \ + __builtin_types_compatible_p(__typeof__(expr), signed short) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned short) || \ + __builtin_types_compatible_p(__typeof__(expr), char) || \ + __builtin_types_compatible_p(__typeof__(expr), signed char) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned char)) + +/* evaluates to true if expr is of type curl_off_t */ +#define _curl_is_off_t(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), curl_off_t)) + +/* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */ +/* XXX: also check size of an char[] array? */ +#define _curl_is_error_buffer(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), char *) || \ + __builtin_types_compatible_p(__typeof__(expr), char[])) + +/* evaluates to true if expr is of type (const) void* or (const) FILE* */ +#if 0 +#define _curl_is_cb_data(expr) \ + (_curl_is_ptr((expr), void) || \ + _curl_is_ptr((expr), FILE)) +#else /* be less strict */ +#define _curl_is_cb_data(expr) \ + _curl_is_any_ptr(expr) +#endif + +/* evaluates to true if expr is of type FILE* */ +#define _curl_is_FILE(expr) \ + (_curl_is_NULL(expr) || \ + (__builtin_types_compatible_p(__typeof__(expr), FILE *))) + +/* evaluates to true if expr can be passed as POST data (void* or char*) */ +#define _curl_is_postfields(expr) \ + (_curl_is_ptr((expr), void) || \ + _curl_is_arr((expr), char)) + +/* FIXME: the whole callback checking is messy... + * The idea is to tolerate char vs. void and const vs. not const + * pointers in arguments at least + */ +/* helper: __builtin_types_compatible_p distinguishes between functions and + * function pointers, hide it */ +#define _curl_callback_compatible(func, type) \ + (__builtin_types_compatible_p(__typeof__(func), type) || \ + __builtin_types_compatible_p(__typeof__(func) *, type)) + +/* evaluates to true if expr is of type curl_read_callback or "similar" */ +#define _curl_is_read_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), __typeof__(fread) *) || \ + _curl_callback_compatible((expr), curl_read_callback) || \ + _curl_callback_compatible((expr), _curl_read_callback1) || \ + _curl_callback_compatible((expr), _curl_read_callback2) || \ + _curl_callback_compatible((expr), _curl_read_callback3) || \ + _curl_callback_compatible((expr), _curl_read_callback4) || \ + _curl_callback_compatible((expr), _curl_read_callback5) || \ + _curl_callback_compatible((expr), _curl_read_callback6)) +typedef size_t (*_curl_read_callback1)(char *, size_t, size_t, void *); +typedef size_t (*_curl_read_callback2)(char *, size_t, size_t, const void *); +typedef size_t (*_curl_read_callback3)(char *, size_t, size_t, FILE *); +typedef size_t (*_curl_read_callback4)(void *, size_t, size_t, void *); +typedef size_t (*_curl_read_callback5)(void *, size_t, size_t, const void *); +typedef size_t (*_curl_read_callback6)(void *, size_t, size_t, FILE *); + +/* evaluates to true if expr is of type curl_write_callback or "similar" */ +#define _curl_is_write_cb(expr) \ + (_curl_is_read_cb(expr) || \ + _curl_callback_compatible((expr), __typeof__(fwrite) *) || \ + _curl_callback_compatible((expr), curl_write_callback) || \ + _curl_callback_compatible((expr), _curl_write_callback1) || \ + _curl_callback_compatible((expr), _curl_write_callback2) || \ + _curl_callback_compatible((expr), _curl_write_callback3) || \ + _curl_callback_compatible((expr), _curl_write_callback4) || \ + _curl_callback_compatible((expr), _curl_write_callback5) || \ + _curl_callback_compatible((expr), _curl_write_callback6)) +typedef size_t (*_curl_write_callback1)(const char *, size_t, size_t, void *); +typedef size_t (*_curl_write_callback2)(const char *, size_t, size_t, + const void *); +typedef size_t (*_curl_write_callback3)(const char *, size_t, size_t, FILE *); +typedef size_t (*_curl_write_callback4)(const void *, size_t, size_t, void *); +typedef size_t (*_curl_write_callback5)(const void *, size_t, size_t, + const void *); +typedef size_t (*_curl_write_callback6)(const void *, size_t, size_t, FILE *); + +/* evaluates to true if expr is of type curl_ioctl_callback or "similar" */ +#define _curl_is_ioctl_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_ioctl_callback) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback1) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback2) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback3) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback4)) +typedef curlioerr (*_curl_ioctl_callback1)(CURL *, int, void *); +typedef curlioerr (*_curl_ioctl_callback2)(CURL *, int, const void *); +typedef curlioerr (*_curl_ioctl_callback3)(CURL *, curliocmd, void *); +typedef curlioerr (*_curl_ioctl_callback4)(CURL *, curliocmd, const void *); + +/* evaluates to true if expr is of type curl_sockopt_callback or "similar" */ +#define _curl_is_sockopt_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_sockopt_callback) || \ + _curl_callback_compatible((expr), _curl_sockopt_callback1) || \ + _curl_callback_compatible((expr), _curl_sockopt_callback2)) +typedef int (*_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype); +typedef int (*_curl_sockopt_callback2)(const void *, curl_socket_t, + curlsocktype); + +/* evaluates to true if expr is of type curl_opensocket_callback or + "similar" */ +#define _curl_is_opensocket_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_opensocket_callback) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback1) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback2) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback3) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback4)) +typedef curl_socket_t (*_curl_opensocket_callback1) + (void *, curlsocktype, struct curl_sockaddr *); +typedef curl_socket_t (*_curl_opensocket_callback2) + (void *, curlsocktype, const struct curl_sockaddr *); +typedef curl_socket_t (*_curl_opensocket_callback3) + (const void *, curlsocktype, struct curl_sockaddr *); +typedef curl_socket_t (*_curl_opensocket_callback4) + (const void *, curlsocktype, const struct curl_sockaddr *); + +/* evaluates to true if expr is of type curl_progress_callback or "similar" */ +#define _curl_is_progress_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_progress_callback) || \ + _curl_callback_compatible((expr), _curl_progress_callback1) || \ + _curl_callback_compatible((expr), _curl_progress_callback2)) +typedef int (*_curl_progress_callback1)(void *, + double, double, double, double); +typedef int (*_curl_progress_callback2)(const void *, + double, double, double, double); + +/* evaluates to true if expr is of type curl_debug_callback or "similar" */ +#define _curl_is_debug_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_debug_callback) || \ + _curl_callback_compatible((expr), _curl_debug_callback1) || \ + _curl_callback_compatible((expr), _curl_debug_callback2) || \ + _curl_callback_compatible((expr), _curl_debug_callback3) || \ + _curl_callback_compatible((expr), _curl_debug_callback4) || \ + _curl_callback_compatible((expr), _curl_debug_callback5) || \ + _curl_callback_compatible((expr), _curl_debug_callback6) || \ + _curl_callback_compatible((expr), _curl_debug_callback7) || \ + _curl_callback_compatible((expr), _curl_debug_callback8)) +typedef int (*_curl_debug_callback1) (CURL *, + curl_infotype, char *, size_t, void *); +typedef int (*_curl_debug_callback2) (CURL *, + curl_infotype, char *, size_t, const void *); +typedef int (*_curl_debug_callback3) (CURL *, + curl_infotype, const char *, size_t, void *); +typedef int (*_curl_debug_callback4) (CURL *, + curl_infotype, const char *, size_t, const void *); +typedef int (*_curl_debug_callback5) (CURL *, + curl_infotype, unsigned char *, size_t, void *); +typedef int (*_curl_debug_callback6) (CURL *, + curl_infotype, unsigned char *, size_t, const void *); +typedef int (*_curl_debug_callback7) (CURL *, + curl_infotype, const unsigned char *, size_t, void *); +typedef int (*_curl_debug_callback8) (CURL *, + curl_infotype, const unsigned char *, size_t, const void *); + +/* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */ +/* this is getting even messier... */ +#define _curl_is_ssl_ctx_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_ssl_ctx_callback) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback1) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback2) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback3) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback4) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback5) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback6) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback7) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback8)) +typedef CURLcode (*_curl_ssl_ctx_callback1)(CURL *, void *, void *); +typedef CURLcode (*_curl_ssl_ctx_callback2)(CURL *, void *, const void *); +typedef CURLcode (*_curl_ssl_ctx_callback3)(CURL *, const void *, void *); +typedef CURLcode (*_curl_ssl_ctx_callback4)(CURL *, const void *, + const void *); +#ifdef HEADER_SSL_H +/* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX + * this will of course break if we're included before OpenSSL headers... + */ +typedef CURLcode (*_curl_ssl_ctx_callback5)(CURL *, SSL_CTX, void *); +typedef CURLcode (*_curl_ssl_ctx_callback6)(CURL *, SSL_CTX, const void *); +typedef CURLcode (*_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX, void *); +typedef CURLcode (*_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX, + const void *); +#else +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback5; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback6; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback7; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8; +#endif + +/* evaluates to true if expr is of type curl_conv_callback or "similar" */ +#define _curl_is_conv_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_conv_callback) || \ + _curl_callback_compatible((expr), _curl_conv_callback1) || \ + _curl_callback_compatible((expr), _curl_conv_callback2) || \ + _curl_callback_compatible((expr), _curl_conv_callback3) || \ + _curl_callback_compatible((expr), _curl_conv_callback4)) +typedef CURLcode (*_curl_conv_callback1)(char *, size_t length); +typedef CURLcode (*_curl_conv_callback2)(const char *, size_t length); +typedef CURLcode (*_curl_conv_callback3)(void *, size_t length); +typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length); + +/* evaluates to true if expr is of type curl_seek_callback or "similar" */ +#define _curl_is_seek_cb(expr) \ + (_curl_is_NULL(expr) || \ + _curl_callback_compatible((expr), curl_seek_callback) || \ + _curl_callback_compatible((expr), _curl_seek_callback1) || \ + _curl_callback_compatible((expr), _curl_seek_callback2)) +typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int); +typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int); + + +#endif /* __CURL_TYPECHECK_GCC_H */ diff --git a/ext/curl-7.58.0/x64/lib/libcurl_a.lib b/ext/curl-7.58.0/x64/lib/libcurl_a.lib new file mode 100644 index 000000000..7efe65b16 Binary files /dev/null and b/ext/curl-7.58.0/x64/lib/libcurl_a.lib differ diff --git a/ext/curl-7.58.0/x64/lib/libcurl_a_debug.lib b/ext/curl-7.58.0/x64/lib/libcurl_a_debug.lib new file mode 100644 index 000000000..867964b2c Binary files /dev/null and b/ext/curl-7.58.0/x64/lib/libcurl_a_debug.lib differ diff --git a/ext/hiredis-0.14.1/.gitignore b/ext/hiredis-0.14.1/.gitignore deleted file mode 100644 index db2ad032a..000000000 --- a/ext/hiredis-0.14.1/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/hiredis-test -/examples/hiredis-example* -/*.o -/*.so -/*.dylib -/*.pc diff --git a/ext/hiredis-0.14.1/.travis.yml b/ext/hiredis-0.14.1/.travis.yml deleted file mode 100644 index faf2ce684..000000000 --- a/ext/hiredis-0.14.1/.travis.yml +++ /dev/null @@ -1,45 +0,0 @@ -language: c -sudo: false -compiler: - - gcc - - clang - -os: - - linux - - osx - -branches: - only: - - staging - - trying - - master - -before_script: - - if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update; brew install redis; fi - -addons: - apt: - packages: - - libc6-dbg - - libc6-dev - - libc6:i386 - - libc6-dev-i386 - - libc6-dbg:i386 - - gcc-multilib - - valgrind - -env: - - CFLAGS="-Werror" - - PRE="valgrind --track-origins=yes --leak-check=full" - - TARGET="32bit" TARGET_VARS="32bit-vars" CFLAGS="-Werror" - - TARGET="32bit" TARGET_VARS="32bit-vars" PRE="valgrind --track-origins=yes --leak-check=full" - -matrix: - exclude: - - os: osx - env: PRE="valgrind --track-origins=yes --leak-check=full" - - - os: osx - env: TARGET="32bit" TARGET_VARS="32bit-vars" PRE="valgrind --track-origins=yes --leak-check=full" - -script: make $TARGET CFLAGS="$CFLAGS" && make check PRE="$PRE" && make $TARGET_VARS hiredis-example diff --git a/ext/hiredis-0.14.1/CHANGELOG.md b/ext/hiredis-0.14.1/CHANGELOG.md deleted file mode 100644 index f8e577369..000000000 --- a/ext/hiredis-0.14.1/CHANGELOG.md +++ /dev/null @@ -1,190 +0,0 @@ -**NOTE: BREAKING CHANGES upgrading from 0.13.x to 0.14.x **: - -* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now - protocol errors. This is consistent with the RESP specification. On 32-bit - platforms, the upper bound is lowered to `SIZE_MAX`. - -* Change `redisReply.len` to `size_t`, as it denotes the the size of a string - - User code should compare this to `size_t` values as well. If it was used to - compare to other values, casting might be necessary or can be removed, if - casting was applied before. - -### 0.14.1 (2020-03-13) - -* Adds safe allocation wrappers (CVE-2020-7105, #747, #752) (Michael Grunder) - -### 0.14.0 (2018-09-25) - -* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b]) -* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537]) -* Use string2ll from Redis w/added tests (Michael Grunder [7bef04, 60f622]) -* Makefile - OSX compilation fixes (Ryan Schmidt [881fcb, 0e9af8]) -* Remove redundant NULL checks (Justin Brewer [54acc8, 58e6b8]) -* Fix bulk and multi-bulk length truncation (Justin Brewer [109197]) -* Fix SIGSEGV in OpenBSD by checking for NULL before calling freeaddrinfo (Justin Brewer [546d94]) -* Several POSIX compatibility fixes (Justin Brewer [bbeab8, 49bbaa, d1c1b6]) -* Makefile - Compatibility fixes (Dimitri Vorobiev [3238cf, 12a9d1]) -* Makefile - Fix make install on FreeBSD (Zach Shipko [a2ef2b]) -* Makefile - don't assume $(INSTALL) is cp (Igor Gnatenko [725a96]) -* Separate side-effect causing function from assert and small cleanup (amallia [b46413, 3c3234]) -* Don't send negative values to `__redisAsyncCommand` (Frederik Deweerdt [706129]) -* Fix leak if setsockopt fails (Frederik Deweerdt [e21c9c]) -* Fix libevent leak (zfz [515228]) -* Clean up GCC warning (Ichito Nagata [2ec774]) -* Keep track of errno in `__redisSetErrorFromErrno()` as snprintf may use it (Jin Qing [25cd88]) -* Solaris compilation fix (Donald Whyte [41b07d]) -* Reorder linker arguments when building examples (Tustfarm-heart [06eedd]) -* Keep track of subscriptions in case of rapid subscribe/unsubscribe (Hyungjin Kim [073dc8, be76c5, d46999]) -* libuv use after free fix (Paul Scott [cbb956]) -* Properly close socket fd on reconnect attempt (WSL [64d1ec]) -* Skip valgrind in OSX tests (Jan-Erik Rediger [9deb78]) -* Various updates for Travis testing OSX (Ted Nyman [fa3774, 16a459, bc0ea5]) -* Update libevent (Chris Xin [386802]) -* Change sds.h for building in C++ projects (Ali Volkan ATLI [f5b32e]) -* Use proper format specifier in redisFormatSdsCommandArgv (Paulino Huerta, Jan-Erik Rediger [360a06, 8655a6]) -* Better handling of NULL reply in example code (Jan-Erik Rediger [1b8ed3]) -* Prevent overflow when formatting an error (Jan-Erik Rediger [0335cb]) -* Compatibility fix for strerror_r (Tom Lee [bb1747]) -* Properly detect integer parse/overflow errors (Justin Brewer [93421f]) -* Adds CI for Windows and cygwin fixes (owent, [6c53d6, 6c3e40]) -* Catch a buffer overflow when formatting the error message -* Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13 -* Fix warnings, when compiled with -Wshadow -* Make hiredis compile in Cygwin on Windows, now CI-tested - -**BREAKING CHANGES**: - -* Remove backwards compatibility macro's - -This removes the following old function aliases, use the new name now: - -| Old | New | -| --------------------------- | ---------------------- | -| redisReplyReaderCreate | redisReaderCreate | -| redisReplyReaderCreate | redisReaderCreate | -| redisReplyReaderFree | redisReaderFree | -| redisReplyReaderFeed | redisReaderFeed | -| redisReplyReaderGetReply | redisReaderGetReply | -| redisReplyReaderSetPrivdata | redisReaderSetPrivdata | -| redisReplyReaderGetObject | redisReaderGetObject | -| redisReplyReaderGetError | redisReaderGetError | - -* The `DEBUG` variable in the Makefile was renamed to `DEBUG_FLAGS` - -Previously it broke some builds for people that had `DEBUG` set to some arbitrary value, -due to debugging other software. -By renaming we avoid unintentional name clashes. - -Simply rename `DEBUG` to `DEBUG_FLAGS` in your environment to make it working again. - -### 0.13.3 (2015-09-16) - -* Revert "Clear `REDIS_CONNECTED` flag when connection is closed". -* Make tests pass on FreeBSD (Thanks, Giacomo Olgeni) - - -If the `REDIS_CONNECTED` flag is cleared, -the async onDisconnect callback function will never be called. -This causes problems as the disconnect is never reported back to the user. - -### 0.13.2 (2015-08-25) - -* Prevent crash on pending replies in async code (Thanks, @switch-st) -* Clear `REDIS_CONNECTED` flag when connection is closed (Thanks, Jerry Jacobs) -* Add MacOS X addapter (Thanks, @dizzus) -* Add Qt adapter (Thanks, Pietro Cerutti) -* Add Ivykis adapter (Thanks, Gergely Nagy) - -All adapters are provided as is and are only tested where possible. - -### 0.13.1 (2015-05-03) - -This is a bug fix release. -The new `reconnect` method introduced new struct members, which clashed with pre-defined names in pre-C99 code. -Another commit forced C99 compilation just to make it work, but of course this is not desirable for outside projects. -Other non-C99 code can now use hiredis as usual again. -Sorry for the inconvenience. - -* Fix memory leak in async reply handling (Salvatore Sanfilippo) -* Rename struct member to avoid name clash with pre-c99 code (Alex Balashov, ncopa) - -### 0.13.0 (2015-04-16) - -This release adds a minimal Windows compatibility layer. -The parser, standalone since v0.12.0, can now be compiled on Windows -(and thus used in other client libraries as well) - -* Windows compatibility layer for parser code (tzickel) -* Properly escape data printed to PKGCONF file (Dan Skorupski) -* Fix tests when assert() undefined (Keith Bennett, Matt Stancliff) -* Implement a reconnect method for the client context, this changes the structure of `redisContext` (Aaron Bedra) - -### 0.12.1 (2015-01-26) - -* Fix `make install`: DESTDIR support, install all required files, install PKGCONF in proper location -* Fix `make test` as 32 bit build on 64 bit platform - -### 0.12.0 (2015-01-22) - -* Add optional KeepAlive support - -* Try again on EINTR errors - -* Add libuv adapter - -* Add IPv6 support - -* Remove possibility of multiple close on same fd - -* Add ability to bind source address on connect - -* Add redisConnectFd() and redisFreeKeepFd() - -* Fix getaddrinfo() memory leak - -* Free string if it is unused (fixes memory leak) - -* Improve redisAppendCommandArgv performance 2.5x - -* Add support for SO_REUSEADDR - -* Fix redisvFormatCommand format parsing - -* Add GLib 2.0 adapter - -* Refactor reading code into read.c - -* Fix errno error buffers to not clobber errors - -* Generate pkgconf during build - -* Silence _BSD_SOURCE warnings - -* Improve digit counting for multibulk creation - - -### 0.11.0 - -* Increase the maximum multi-bulk reply depth to 7. - -* Increase the read buffer size from 2k to 16k. - -* Use poll(2) instead of select(2) to support large fds (>= 1024). - -### 0.10.1 - -* Makefile overhaul. Important to check out if you override one or more - variables using environment variables or via arguments to the "make" tool. - -* Issue #45: Fix potential memory leak for a multi bulk reply with 0 elements - being created by the default reply object functions. - -* Issue #43: Don't crash in an asynchronous context when Redis returns an error - reply after the connection has been made (this happens when the maximum - number of connections is reached). - -### 0.10.0 - -* See commit log. - diff --git a/ext/hiredis-0.14.1/COPYING b/ext/hiredis-0.14.1/COPYING deleted file mode 100644 index a5fc97395..000000000 --- a/ext/hiredis-0.14.1/COPYING +++ /dev/null @@ -1,29 +0,0 @@ -Copyright (c) 2009-2011, Salvatore Sanfilippo -Copyright (c) 2010-2011, Pieter Noordhuis - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of Redis nor the names of its contributors may be used - to endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ext/hiredis-0.14.1/Makefile b/ext/hiredis-0.14.1/Makefile deleted file mode 100644 index d1f005af4..000000000 --- a/ext/hiredis-0.14.1/Makefile +++ /dev/null @@ -1,214 +0,0 @@ -# Hiredis Makefile -# Copyright (C) 2010-2011 Salvatore Sanfilippo -# Copyright (C) 2010-2011 Pieter Noordhuis -# This file is released under the BSD license, see the COPYING file - -OBJ=net.o hiredis.o sds.o async.o read.o alloc.o -EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib -TESTS=hiredis-test -LIBNAME=libhiredis -PKGCONFNAME=hiredis.pc - -HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}') -HIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}') -HIREDIS_PATCH=$(shell grep HIREDIS_PATCH hiredis.h | awk '{print $$3}') -HIREDIS_SONAME=$(shell grep HIREDIS_SONAME hiredis.h | awk '{print $$3}') - -# Installation related variables and target -PREFIX?=/usr/local -INCLUDE_PATH?=include/hiredis -LIBRARY_PATH?=lib -PKGCONF_PATH?=pkgconfig -INSTALL_INCLUDE_PATH= $(DESTDIR)$(PREFIX)/$(INCLUDE_PATH) -INSTALL_LIBRARY_PATH= $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH) -INSTALL_PKGCONF_PATH= $(INSTALL_LIBRARY_PATH)/$(PKGCONF_PATH) - -# redis-server configuration used for testing -REDIS_PORT=56379 -REDIS_SERVER=redis-server -define REDIS_TEST_CONFIG - daemonize yes - pidfile /tmp/hiredis-test-redis.pid - port $(REDIS_PORT) - bind 127.0.0.1 - unixsocket /tmp/hiredis-test-redis.sock -endef -export REDIS_TEST_CONFIG - -# Fallback to gcc when $CC is not in $PATH. -CC:=$(shell sh -c 'type $${CC%% *} >/dev/null 2>/dev/null && echo $(CC) || echo gcc') -CXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++') -OPTIMIZATION?=-O3 -WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings -DEBUG_FLAGS?= -g -ggdb -REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS) -REAL_LDFLAGS=$(LDFLAGS) - -DYLIBSUFFIX=so -STLIBSUFFIX=a -DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME) -DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR) -DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX) -DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS) -STLIBNAME=$(LIBNAME).$(STLIBSUFFIX) -STLIB_MAKE_CMD=ar rcs $(STLIBNAME) - -# Platform-specific overrides -uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') -ifeq ($(uname_S),SunOS) - REAL_LDFLAGS+= -ldl -lnsl -lsocket - DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS) -endif -ifeq ($(uname_S),Darwin) - DYLIBSUFFIX=dylib - DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX) - DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS) -endif - -all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME) - -# Deps (use make dep to generate this) -alloc.o: alloc.c fmacros.h alloc.h -async.o: async.c fmacros.h alloc.h async.h hiredis.h read.h sds.h net.h dict.c dict.h -dict.o: dict.c fmacros.h alloc.h dict.h -hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h alloc.h net.h -net.o: net.c fmacros.h net.h hiredis.h read.h sds.h alloc.h -read.o: read.c fmacros.h read.h sds.h -sds.o: sds.c sds.h sdsalloc.h -test.o: test.c fmacros.h hiredis.h read.h sds.h alloc.h net.h - -$(DYLIBNAME): $(OBJ) - $(DYLIB_MAKE_CMD) $(OBJ) - -$(STLIBNAME): $(OBJ) - $(STLIB_MAKE_CMD) $(OBJ) - -dynamic: $(DYLIBNAME) -static: $(STLIBNAME) - -# Binaries: -hiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -levent $(STLIBNAME) - -hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -lev $(STLIBNAME) - -hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME) - -hiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -livykis $(STLIBNAME) - -hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -framework CoreFoundation $(STLIBNAME) - -ifndef AE_DIR -hiredis-example-ae: - @echo "Please specify AE_DIR (e.g. /src)" - @false -else -hiredis-example-ae: examples/example-ae.c adapters/ae.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(AE_DIR) $< $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o $(AE_DIR)/../deps/jemalloc/lib/libjemalloc.a -pthread $(STLIBNAME) -endif - -ifndef LIBUV_DIR -hiredis-example-libuv: - @echo "Please specify LIBUV_DIR (e.g. ../libuv/)" - @false -else -hiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread -lrt $(STLIBNAME) -endif - -ifeq ($(and $(QT_MOC),$(QT_INCLUDE_DIR),$(QT_LIBRARY_DIR)),) -hiredis-example-qt: - @echo "Please specify QT_MOC, QT_INCLUDE_DIR AND QT_LIBRARY_DIR" - @false -else -hiredis-example-qt: examples/example-qt.cpp adapters/qt.h $(STLIBNAME) - $(QT_MOC) adapters/qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \ - $(CXX) -x c++ -o qt-adapter-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore - $(QT_MOC) examples/example-qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \ - $(CXX) -x c++ -o qt-example-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore - $(CXX) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore -L$(QT_LIBRARY_DIR) qt-adapter-moc.o qt-example-moc.o $< -pthread $(STLIBNAME) -lQtCore -endif - -hiredis-example: examples/example.c $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< $(STLIBNAME) - -examples: $(EXAMPLES) - -hiredis-test: test.o $(STLIBNAME) - -hiredis-%: %.o $(STLIBNAME) - $(CC) $(REAL_CFLAGS) -o $@ $(REAL_LDFLAGS) $< $(STLIBNAME) - -test: hiredis-test - ./hiredis-test - -check: hiredis-test - @echo "$$REDIS_TEST_CONFIG" | $(REDIS_SERVER) - - $(PRE) ./hiredis-test -h 127.0.0.1 -p $(REDIS_PORT) -s /tmp/hiredis-test-redis.sock || \ - ( kill `cat /tmp/hiredis-test-redis.pid` && false ) - kill `cat /tmp/hiredis-test-redis.pid` - -.c.o: - $(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $< - -clean: - rm -rf $(DYLIBNAME) $(STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov - -dep: - $(CC) -MM *.c - -INSTALL?= cp -pPR - -$(PKGCONFNAME): hiredis.h - @echo "Generating $@ for pkgconfig..." - @echo prefix=$(PREFIX) > $@ - @echo exec_prefix=\$${prefix} >> $@ - @echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@ - @echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@ - @echo >> $@ - @echo Name: hiredis >> $@ - @echo Description: Minimalistic C client library for Redis. >> $@ - @echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@ - @echo Libs: -L\$${libdir} -lhiredis >> $@ - @echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@ - -install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME) - mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH) - $(INSTALL) hiredis.h async.h read.h sds.h alloc.h $(INSTALL_INCLUDE_PATH) - $(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters - $(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME) - cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME) - $(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH) - mkdir -p $(INSTALL_PKGCONF_PATH) - $(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH) - -32bit: - @echo "" - @echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386" - @echo "" - $(MAKE) CFLAGS="-m32" LDFLAGS="-m32" - -32bit-vars: - $(eval CFLAGS=-m32) - $(eval LDFLAGS=-m32) - -gprof: - $(MAKE) CFLAGS="-pg" LDFLAGS="-pg" - -gcov: - $(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs" - -coverage: gcov - make check - mkdir -p tmp/lcov - lcov -d . -c -o tmp/lcov/hiredis.info - genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info - -noopt: - $(MAKE) OPTIMIZATION="" - -.PHONY: all test check clean dep install 32bit 32bit-vars gprof gcov noopt diff --git a/ext/hiredis-0.14.1/README.md b/ext/hiredis-0.14.1/README.md deleted file mode 100644 index 50e2e6be6..000000000 --- a/ext/hiredis-0.14.1/README.md +++ /dev/null @@ -1,410 +0,0 @@ -[![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis) - -**This Readme reflects the latest changed in the master branch. See [v0.14.1](https://github.com/redis/hiredis/tree/v0.14.1) for the Readme and documentation for the latest release.** - -# HIREDIS - -Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) database. - -It is minimalistic because it just adds minimal support for the protocol, but -at the same time it uses a high level printf-alike API in order to make it -much higher level than otherwise suggested by its minimal code base and the -lack of explicit bindings for every Redis command. - -Apart from supporting sending commands and receiving replies, it comes with -a reply parser that is decoupled from the I/O layer. It -is a stream parser designed for easy reusability, which can for instance be used -in higher level language bindings for efficient reply parsing. - -Hiredis only supports the binary-safe Redis protocol, so you can use it with any -Redis version >= 1.2.0. - -The library comes with multiple APIs. There is the -*synchronous API*, the *asynchronous API* and the *reply parsing API*. - -## IMPORTANT: Breaking changes when upgrading from 0.13.x -> 0.14.x - -Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now -protocol errors. This is consistent with the RESP specification. On 32-bit -platforms, the upper bound is lowered to `SIZE_MAX`. - -Change `redisReply.len` to `size_t`, as it denotes the the size of a string - -User code should compare this to `size_t` values as well. If it was used to -compare to other values, casting might be necessary or can be removed, if -casting was applied before. - -For a detailed list of changes please view our [Changelog](CHANGELOG.md). - -## Synchronous API - -To consume the synchronous API, there are only a few function calls that need to be introduced: - -```c -redisContext *redisConnect(const char *ip, int port); -void *redisCommand(redisContext *c, const char *format, ...); -void freeReplyObject(void *reply); -``` - -### Connecting - -The function `redisConnect` is used to create a so-called `redisContext`. The -context is where Hiredis holds state for a connection. The `redisContext` -struct has an integer `err` field that is non-zero when the connection is in -an error state. The field `errstr` will contain a string with a description of -the error. More information on errors can be found in the **Errors** section. -After trying to connect to Redis using `redisConnect` you should -check the `err` field to see if establishing the connection was successful: -```c -redisContext *c = redisConnect("127.0.0.1", 6379); -if (c == NULL || c->err) { - if (c) { - printf("Error: %s\n", c->errstr); - // handle error - } else { - printf("Can't allocate redis context\n"); - } -} -``` - -*Note: A `redisContext` is not thread-safe.* - -### Sending commands - -There are several ways to issue commands to Redis. The first that will be introduced is -`redisCommand`. This function takes a format similar to printf. In the simplest form, -it is used like this: -```c -reply = redisCommand(context, "SET foo bar"); -``` - -The specifier `%s` interpolates a string in the command, and uses `strlen` to -determine the length of the string: -```c -reply = redisCommand(context, "SET foo %s", value); -``` -When you need to pass binary safe strings in a command, the `%b` specifier can be -used. Together with a pointer to the string, it requires a `size_t` length argument -of the string: -```c -reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen); -``` -Internally, Hiredis splits the command in different arguments and will -convert it to the protocol used to communicate with Redis. -One or more spaces separates arguments, so you can use the specifiers -anywhere in an argument: -```c -reply = redisCommand(context, "SET key:%s %s", myid, value); -``` - -### Using replies - -The return value of `redisCommand` holds a reply when the command was -successfully executed. When an error occurs, the return value is `NULL` and -the `err` field in the context will be set (see section on **Errors**). -Once an error is returned the context cannot be reused and you should set up -a new connection. - -The standard replies that `redisCommand` are of the type `redisReply`. The -`type` field in the `redisReply` should be used to test what kind of reply -was received: - -* **`REDIS_REPLY_STATUS`**: - * The command replied with a status reply. The status string can be accessed using `reply->str`. - The length of this string can be accessed using `reply->len`. - -* **`REDIS_REPLY_ERROR`**: - * The command replied with an error. The error string can be accessed identical to `REDIS_REPLY_STATUS`. - -* **`REDIS_REPLY_INTEGER`**: - * The command replied with an integer. The integer value can be accessed using the - `reply->integer` field of type `long long`. - -* **`REDIS_REPLY_NIL`**: - * The command replied with a **nil** object. There is no data to access. - -* **`REDIS_REPLY_STRING`**: - * A bulk (string) reply. The value of the reply can be accessed using `reply->str`. - The length of this string can be accessed using `reply->len`. - -* **`REDIS_REPLY_ARRAY`**: - * A multi bulk reply. The number of elements in the multi bulk reply is stored in - `reply->elements`. Every element in the multi bulk reply is a `redisReply` object as well - and can be accessed via `reply->element[..index..]`. - Redis may reply with nested arrays but this is fully supported. - -Replies should be freed using the `freeReplyObject()` function. -Note that this function will take care of freeing sub-reply objects -contained in arrays and nested arrays, so there is no need for the user to -free the sub replies (it is actually harmful and will corrupt the memory). - -**Important:** the current version of hiredis (0.10.0) frees replies when the -asynchronous API is used. This means you should not call `freeReplyObject` when -you use this API. The reply is cleaned up by hiredis _after_ the callback -returns. This behavior will probably change in future releases, so make sure to -keep an eye on the changelog when upgrading (see issue #39). - -### Cleaning up - -To disconnect and free the context the following function can be used: -```c -void redisFree(redisContext *c); -``` -This function immediately closes the socket and then frees the allocations done in -creating the context. - -### Sending commands (cont'd) - -Together with `redisCommand`, the function `redisCommandArgv` can be used to issue commands. -It has the following prototype: -```c -void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); -``` -It takes the number of arguments `argc`, an array of strings `argv` and the lengths of the -arguments `argvlen`. For convenience, `argvlen` may be set to `NULL` and the function will -use `strlen(3)` on every argument to determine its length. Obviously, when any of the arguments -need to be binary safe, the entire array of lengths `argvlen` should be provided. - -The return value has the same semantic as `redisCommand`. - -### Pipelining - -To explain how Hiredis supports pipelining in a blocking connection, there needs to be -understanding of the internal execution flow. - -When any of the functions in the `redisCommand` family is called, Hiredis first formats the -command according to the Redis protocol. The formatted command is then put in the output buffer -of the context. This output buffer is dynamic, so it can hold any number of commands. -After the command is put in the output buffer, `redisGetReply` is called. This function has the -following two execution paths: - -1. The input buffer is non-empty: - * Try to parse a single reply from the input buffer and return it - * If no reply could be parsed, continue at *2* -2. The input buffer is empty: - * Write the **entire** output buffer to the socket - * Read from the socket until a single reply could be parsed - -The function `redisGetReply` is exported as part of the Hiredis API and can be used when a reply -is expected on the socket. To pipeline commands, the only things that needs to be done is -filling up the output buffer. For this cause, two commands can be used that are identical -to the `redisCommand` family, apart from not returning a reply: -```c -void redisAppendCommand(redisContext *c, const char *format, ...); -void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); -``` -After calling either function one or more times, `redisGetReply` can be used to receive the -subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where -the latter means an error occurred while reading a reply. Just as with the other commands, -the `err` field in the context can be used to find out what the cause of this error is. - -The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and -a single call to `read(2)`): -```c -redisReply *reply; -redisAppendCommand(context,"SET foo bar"); -redisAppendCommand(context,"GET foo"); -redisGetReply(context,&reply); // reply for SET -freeReplyObject(reply); -redisGetReply(context,&reply); // reply for GET -freeReplyObject(reply); -``` -This API can also be used to implement a blocking subscriber: -```c -reply = redisCommand(context,"SUBSCRIBE foo"); -freeReplyObject(reply); -while(redisGetReply(context,&reply) == REDIS_OK) { - // consume message - freeReplyObject(reply); -} -``` -### Errors - -When a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is -returned. The `err` field inside the context will be non-zero and set to one of the -following constants: - -* **`REDIS_ERR_IO`**: - There was an I/O error while creating the connection, trying to write - to the socket or read from the socket. If you included `errno.h` in your - application, you can use the global `errno` variable to find out what is - wrong. - -* **`REDIS_ERR_EOF`**: - The server closed the connection which resulted in an empty read. - -* **`REDIS_ERR_PROTOCOL`**: - There was an error while parsing the protocol. - -* **`REDIS_ERR_OTHER`**: - Any other error. Currently, it is only used when a specified hostname to connect - to cannot be resolved. - -In every case, the `errstr` field in the context will be set to hold a string representation -of the error. - -## Asynchronous API - -Hiredis comes with an asynchronous API that works easily with any event library. -Examples are bundled that show using Hiredis with [libev](http://software.schmorp.de/pkg/libev.html) -and [libevent](http://monkey.org/~provos/libevent/). - -### Connecting - -The function `redisAsyncConnect` can be used to establish a non-blocking connection to -Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field -should be checked after creation to see if there were errors creating the connection. -Because the connection that will be created is non-blocking, the kernel is not able to -instantly return if the specified host and port is able to accept a connection. - -*Note: A `redisAsyncContext` is not thread-safe.* - -```c -redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); -if (c->err) { - printf("Error: %s\n", c->errstr); - // handle error -} -``` - -The asynchronous context can hold a disconnect callback function that is called when the -connection is disconnected (either because of an error or per user request). This function should -have the following prototype: -```c -void(const redisAsyncContext *c, int status); -``` -On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the -user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err` -field in the context can be accessed to find out the cause of the error. - -The context object is always freed after the disconnect callback fired. When a reconnect is needed, -the disconnect callback is a good point to do so. - -Setting the disconnect callback can only be done once per context. For subsequent calls it will -return `REDIS_ERR`. The function to set the disconnect callback has the following prototype: -```c -int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); -``` -### Sending commands and their callbacks - -In an asynchronous context, commands are automatically pipelined due to the nature of an event loop. -Therefore, unlike the synchronous API, there is only a single way to send commands. -Because commands are sent to Redis asynchronously, issuing a command requires a callback function -that is called when the reply is received. Reply callbacks should have the following prototype: -```c -void(redisAsyncContext *c, void *reply, void *privdata); -``` -The `privdata` argument can be used to curry arbitrary data to the callback from the point where -the command is initially queued for execution. - -The functions that can be used to issue commands in an asynchronous context are: -```c -int redisAsyncCommand( - redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, - const char *format, ...); -int redisAsyncCommandArgv( - redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, - int argc, const char **argv, const size_t *argvlen); -``` -Both functions work like their blocking counterparts. The return value is `REDIS_OK` when the command -was successfully added to the output buffer and `REDIS_ERR` otherwise. Example: when the connection -is being disconnected per user-request, no new commands may be added to the output buffer and `REDIS_ERR` is -returned on calls to the `redisAsyncCommand` family. - -If the reply for a command with a `NULL` callback is read, it is immediately freed. When the callback -for a command is non-`NULL`, the memory is freed immediately following the callback: the reply is only -valid for the duration of the callback. - -All pending callbacks are called with a `NULL` reply when the context encountered an error. - -### Disconnecting - -An asynchronous connection can be terminated using: -```c -void redisAsyncDisconnect(redisAsyncContext *ac); -``` -When this function is called, the connection is **not** immediately terminated. Instead, new -commands are no longer accepted and the connection is only terminated when all pending commands -have been written to the socket, their respective replies have been read and their respective -callbacks have been executed. After this, the disconnection callback is executed with the -`REDIS_OK` status and the context object is freed. - -### Hooking it up to event library *X* - -There are a few hooks that need to be set on the context object after it is created. -See the `adapters/` directory for bindings to *libev* and *libevent*. - -## Reply parsing API - -Hiredis comes with a reply parsing API that makes it easy for writing higher -level language bindings. - -The reply parsing API consists of the following functions: -```c -redisReader *redisReaderCreate(void); -void redisReaderFree(redisReader *reader); -int redisReaderFeed(redisReader *reader, const char *buf, size_t len); -int redisReaderGetReply(redisReader *reader, void **reply); -``` -The same set of functions are used internally by hiredis when creating a -normal Redis context, the above API just exposes it to the user for a direct -usage. - -### Usage - -The function `redisReaderCreate` creates a `redisReader` structure that holds a -buffer with unparsed data and state for the protocol parser. - -Incoming data -- most likely from a socket -- can be placed in the internal -buffer of the `redisReader` using `redisReaderFeed`. This function will make a -copy of the buffer pointed to by `buf` for `len` bytes. This data is parsed -when `redisReaderGetReply` is called. This function returns an integer status -and a reply object (as described above) via `void **reply`. The returned status -can be either `REDIS_OK` or `REDIS_ERR`, where the latter means something went -wrong (either a protocol error, or an out of memory error). - -The parser limits the level of nesting for multi bulk payloads to 7. If the -multi bulk nesting level is higher than this, the parser returns an error. - -### Customizing replies - -The function `redisReaderGetReply` creates `redisReply` and makes the function -argument `reply` point to the created `redisReply` variable. For instance, if -the response of type `REDIS_REPLY_STATUS` then the `str` field of `redisReply` -will hold the status as a vanilla C string. However, the functions that are -responsible for creating instances of the `redisReply` can be customized by -setting the `fn` field on the `redisReader` struct. This should be done -immediately after creating the `redisReader`. - -For example, [hiredis-rb](https://github.com/pietern/hiredis-rb/blob/master/ext/hiredis_ext/reader.c) -uses customized reply object functions to create Ruby objects. - -### Reader max buffer - -Both when using the Reader API directly or when using it indirectly via a -normal Redis context, the redisReader structure uses a buffer in order to -accumulate data from the server. -Usually this buffer is destroyed when it is empty and is larger than 16 -KiB in order to avoid wasting memory in unused buffers - -However when working with very big payloads destroying the buffer may slow -down performances considerably, so it is possible to modify the max size of -an idle buffer changing the value of the `maxbuf` field of the reader structure -to the desired value. The special value of 0 means that there is no maximum -value for an idle buffer, so the buffer will never get freed. - -For instance if you have a normal Redis context you can set the maximum idle -buffer to zero (unlimited) just with: -```c -context->reader->maxbuf = 0; -``` -This should be done only in order to maximize performances when working with -large payloads. The context should be set back to `REDIS_READER_MAX_BUF` again -as soon as possible in order to prevent allocation of useless memory. - -## AUTHORS - -Hiredis was written by Salvatore Sanfilippo (antirez at gmail) and -Pieter Noordhuis (pcnoordhuis at gmail) and is released under the BSD license. -Hiredis is currently maintained by Matt Stancliff (matt at genges dot com) and -Jan-Erik Rediger (janerik at fnordig dot com) diff --git a/ext/hiredis-0.14.1/adapters/ae.h b/ext/hiredis-0.14.1/adapters/ae.h deleted file mode 100644 index 03939928d..000000000 --- a/ext/hiredis-0.14.1/adapters/ae.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_AE_H__ -#define __HIREDIS_AE_H__ -#include -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisAeEvents { - redisAsyncContext *context; - aeEventLoop *loop; - int fd; - int reading, writing; -} redisAeEvents; - -static void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) { - ((void)el); ((void)fd); ((void)mask); - - redisAeEvents *e = (redisAeEvents*)privdata; - redisAsyncHandleRead(e->context); -} - -static void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) { - ((void)el); ((void)fd); ((void)mask); - - redisAeEvents *e = (redisAeEvents*)privdata; - redisAsyncHandleWrite(e->context); -} - -static void redisAeAddRead(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (!e->reading) { - e->reading = 1; - aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e); - } -} - -static void redisAeDelRead(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (e->reading) { - e->reading = 0; - aeDeleteFileEvent(loop,e->fd,AE_READABLE); - } -} - -static void redisAeAddWrite(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (!e->writing) { - e->writing = 1; - aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e); - } -} - -static void redisAeDelWrite(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (e->writing) { - e->writing = 0; - aeDeleteFileEvent(loop,e->fd,AE_WRITABLE); - } -} - -static void redisAeCleanup(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - redisAeDelRead(privdata); - redisAeDelWrite(privdata); - free(e); -} - -static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisAeEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisAeEvents*)hi_malloc(sizeof(*e)); - e->context = ac; - e->loop = loop; - e->fd = c->fd; - e->reading = e->writing = 0; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisAeAddRead; - ac->ev.delRead = redisAeDelRead; - ac->ev.addWrite = redisAeAddWrite; - ac->ev.delWrite = redisAeDelWrite; - ac->ev.cleanup = redisAeCleanup; - ac->ev.data = e; - - return REDIS_OK; -} -#endif diff --git a/ext/hiredis-0.14.1/adapters/glib.h b/ext/hiredis-0.14.1/adapters/glib.h deleted file mode 100644 index e0a6411d3..000000000 --- a/ext/hiredis-0.14.1/adapters/glib.h +++ /dev/null @@ -1,153 +0,0 @@ -#ifndef __HIREDIS_GLIB_H__ -#define __HIREDIS_GLIB_H__ - -#include - -#include "../hiredis.h" -#include "../async.h" - -typedef struct -{ - GSource source; - redisAsyncContext *ac; - GPollFD poll_fd; -} RedisSource; - -static void -redis_source_add_read (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events |= G_IO_IN; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_del_read (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events &= ~G_IO_IN; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_add_write (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events |= G_IO_OUT; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_del_write (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events &= ~G_IO_OUT; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_cleanup (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - - g_return_if_fail(source); - - redis_source_del_read(source); - redis_source_del_write(source); - /* - * It is not our responsibility to remove ourself from the - * current main loop. However, we will remove the GPollFD. - */ - if (source->poll_fd.fd >= 0) { - g_source_remove_poll((GSource *)data, &source->poll_fd); - source->poll_fd.fd = -1; - } -} - -static gboolean -redis_source_prepare (GSource *source, - gint *timeout_) -{ - RedisSource *redis = (RedisSource *)source; - *timeout_ = -1; - return !!(redis->poll_fd.events & redis->poll_fd.revents); -} - -static gboolean -redis_source_check (GSource *source) -{ - RedisSource *redis = (RedisSource *)source; - return !!(redis->poll_fd.events & redis->poll_fd.revents); -} - -static gboolean -redis_source_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) -{ - RedisSource *redis = (RedisSource *)source; - - if ((redis->poll_fd.revents & G_IO_OUT)) { - redisAsyncHandleWrite(redis->ac); - redis->poll_fd.revents &= ~G_IO_OUT; - } - - if ((redis->poll_fd.revents & G_IO_IN)) { - redisAsyncHandleRead(redis->ac); - redis->poll_fd.revents &= ~G_IO_IN; - } - - if (callback) { - return callback(user_data); - } - - return TRUE; -} - -static void -redis_source_finalize (GSource *source) -{ - RedisSource *redis = (RedisSource *)source; - - if (redis->poll_fd.fd >= 0) { - g_source_remove_poll(source, &redis->poll_fd); - redis->poll_fd.fd = -1; - } -} - -static GSource * -redis_source_new (redisAsyncContext *ac) -{ - static GSourceFuncs source_funcs = { - .prepare = redis_source_prepare, - .check = redis_source_check, - .dispatch = redis_source_dispatch, - .finalize = redis_source_finalize, - }; - redisContext *c = &ac->c; - RedisSource *source; - - g_return_val_if_fail(ac != NULL, NULL); - - source = (RedisSource *)g_source_new(&source_funcs, sizeof *source); - source->ac = ac; - source->poll_fd.fd = c->fd; - source->poll_fd.events = 0; - source->poll_fd.revents = 0; - g_source_add_poll((GSource *)source, &source->poll_fd); - - ac->ev.addRead = redis_source_add_read; - ac->ev.delRead = redis_source_del_read; - ac->ev.addWrite = redis_source_add_write; - ac->ev.delWrite = redis_source_del_write; - ac->ev.cleanup = redis_source_cleanup; - ac->ev.data = source; - - return (GSource *)source; -} - -#endif /* __HIREDIS_GLIB_H__ */ diff --git a/ext/hiredis-0.14.1/adapters/ivykis.h b/ext/hiredis-0.14.1/adapters/ivykis.h deleted file mode 100644 index 75616ee24..000000000 --- a/ext/hiredis-0.14.1/adapters/ivykis.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef __HIREDIS_IVYKIS_H__ -#define __HIREDIS_IVYKIS_H__ -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisIvykisEvents { - redisAsyncContext *context; - struct iv_fd fd; -} redisIvykisEvents; - -static void redisIvykisReadEvent(void *arg) { - redisAsyncContext *context = (redisAsyncContext *)arg; - redisAsyncHandleRead(context); -} - -static void redisIvykisWriteEvent(void *arg) { - redisAsyncContext *context = (redisAsyncContext *)arg; - redisAsyncHandleWrite(context); -} - -static void redisIvykisAddRead(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent); -} - -static void redisIvykisDelRead(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_in(&e->fd, NULL); -} - -static void redisIvykisAddWrite(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent); -} - -static void redisIvykisDelWrite(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_out(&e->fd, NULL); -} - -static void redisIvykisCleanup(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - - iv_fd_unregister(&e->fd); - free(e); -} - -static int redisIvykisAttach(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisIvykisEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisIvykisEvents*)hi_malloc(sizeof(*e)); - e->context = ac; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisIvykisAddRead; - ac->ev.delRead = redisIvykisDelRead; - ac->ev.addWrite = redisIvykisAddWrite; - ac->ev.delWrite = redisIvykisDelWrite; - ac->ev.cleanup = redisIvykisCleanup; - ac->ev.data = e; - - /* Initialize and install read/write events */ - IV_FD_INIT(&e->fd); - e->fd.fd = c->fd; - e->fd.handler_in = redisIvykisReadEvent; - e->fd.handler_out = redisIvykisWriteEvent; - e->fd.handler_err = NULL; - e->fd.cookie = e->context; - - iv_fd_register(&e->fd); - - return REDIS_OK; -} -#endif diff --git a/ext/hiredis-0.14.1/adapters/libev.h b/ext/hiredis-0.14.1/adapters/libev.h deleted file mode 100644 index abad43634..000000000 --- a/ext/hiredis-0.14.1/adapters/libev.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_LIBEV_H__ -#define __HIREDIS_LIBEV_H__ -#include -#include -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisLibevEvents { - redisAsyncContext *context; - struct ev_loop *loop; - int reading, writing; - ev_io rev, wev; -} redisLibevEvents; - -static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) { -#if EV_MULTIPLICITY - ((void)loop); -#endif - ((void)revents); - - redisLibevEvents *e = (redisLibevEvents*)watcher->data; - redisAsyncHandleRead(e->context); -} - -static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) { -#if EV_MULTIPLICITY - ((void)loop); -#endif - ((void)revents); - - redisLibevEvents *e = (redisLibevEvents*)watcher->data; - redisAsyncHandleWrite(e->context); -} - -static void redisLibevAddRead(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (!e->reading) { - e->reading = 1; - ev_io_start(EV_A_ &e->rev); - } -} - -static void redisLibevDelRead(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (e->reading) { - e->reading = 0; - ev_io_stop(EV_A_ &e->rev); - } -} - -static void redisLibevAddWrite(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (!e->writing) { - e->writing = 1; - ev_io_start(EV_A_ &e->wev); - } -} - -static void redisLibevDelWrite(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (e->writing) { - e->writing = 0; - ev_io_stop(EV_A_ &e->wev); - } -} - -static void redisLibevCleanup(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - redisLibevDelRead(privdata); - redisLibevDelWrite(privdata); - free(e); -} - -static int redisLibevAttach(EV_P_ redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisLibevEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisLibevEvents*)hi_malloc(sizeof(*e)); - e->context = ac; -#if EV_MULTIPLICITY - e->loop = loop; -#else - e->loop = NULL; -#endif - e->reading = e->writing = 0; - e->rev.data = e; - e->wev.data = e; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisLibevAddRead; - ac->ev.delRead = redisLibevDelRead; - ac->ev.addWrite = redisLibevAddWrite; - ac->ev.delWrite = redisLibevDelWrite; - ac->ev.cleanup = redisLibevCleanup; - ac->ev.data = e; - - /* Initialize read/write events */ - ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ); - ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE); - return REDIS_OK; -} - -#endif diff --git a/ext/hiredis-0.14.1/adapters/libevent.h b/ext/hiredis-0.14.1/adapters/libevent.h deleted file mode 100644 index f2330d6f0..000000000 --- a/ext/hiredis-0.14.1/adapters/libevent.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_LIBEVENT_H__ -#define __HIREDIS_LIBEVENT_H__ -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisLibeventEvents { - redisAsyncContext *context; - struct event *rev, *wev; -} redisLibeventEvents; - -static void redisLibeventReadEvent(int fd, short event, void *arg) { - ((void)fd); ((void)event); - redisLibeventEvents *e = (redisLibeventEvents*)arg; - redisAsyncHandleRead(e->context); -} - -static void redisLibeventWriteEvent(int fd, short event, void *arg) { - ((void)fd); ((void)event); - redisLibeventEvents *e = (redisLibeventEvents*)arg; - redisAsyncHandleWrite(e->context); -} - -static void redisLibeventAddRead(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_add(e->rev,NULL); -} - -static void redisLibeventDelRead(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_del(e->rev); -} - -static void redisLibeventAddWrite(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_add(e->wev,NULL); -} - -static void redisLibeventDelWrite(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_del(e->wev); -} - -static void redisLibeventCleanup(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_free(e->rev); - event_free(e->wev); - free(e); -} - -static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) { - redisContext *c = &(ac->c); - redisLibeventEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisLibeventEvents*)hi_calloc(1, sizeof(*e)); - e->context = ac; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisLibeventAddRead; - ac->ev.delRead = redisLibeventDelRead; - ac->ev.addWrite = redisLibeventAddWrite; - ac->ev.delWrite = redisLibeventDelWrite; - ac->ev.cleanup = redisLibeventCleanup; - ac->ev.data = e; - - /* Initialize and install read/write events */ - e->rev = event_new(base, c->fd, EV_READ, redisLibeventReadEvent, e); - e->wev = event_new(base, c->fd, EV_WRITE, redisLibeventWriteEvent, e); - event_add(e->rev, NULL); - event_add(e->wev, NULL); - return REDIS_OK; -} -#endif diff --git a/ext/hiredis-0.14.1/adapters/libuv.h b/ext/hiredis-0.14.1/adapters/libuv.h deleted file mode 100644 index ff08c25e1..000000000 --- a/ext/hiredis-0.14.1/adapters/libuv.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef __HIREDIS_LIBUV_H__ -#define __HIREDIS_LIBUV_H__ -#include -#include -#include "../hiredis.h" -#include "../async.h" -#include - -typedef struct redisLibuvEvents { - redisAsyncContext* context; - uv_poll_t handle; - int events; -} redisLibuvEvents; - - -static void redisLibuvPoll(uv_poll_t* handle, int status, int events) { - redisLibuvEvents* p = (redisLibuvEvents*)handle->data; - - if (status != 0) { - return; - } - - if (p->context != NULL && (events & UV_READABLE)) { - redisAsyncHandleRead(p->context); - } - if (p->context != NULL && (events & UV_WRITABLE)) { - redisAsyncHandleWrite(p->context); - } -} - - -static void redisLibuvAddRead(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events |= UV_READABLE; - - uv_poll_start(&p->handle, p->events, redisLibuvPoll); -} - - -static void redisLibuvDelRead(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events &= ~UV_READABLE; - - if (p->events) { - uv_poll_start(&p->handle, p->events, redisLibuvPoll); - } else { - uv_poll_stop(&p->handle); - } -} - - -static void redisLibuvAddWrite(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events |= UV_WRITABLE; - - uv_poll_start(&p->handle, p->events, redisLibuvPoll); -} - - -static void redisLibuvDelWrite(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events &= ~UV_WRITABLE; - - if (p->events) { - uv_poll_start(&p->handle, p->events, redisLibuvPoll); - } else { - uv_poll_stop(&p->handle); - } -} - - -static void on_close(uv_handle_t* handle) { - redisLibuvEvents* p = (redisLibuvEvents*)handle->data; - - free(p); -} - - -static void redisLibuvCleanup(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->context = NULL; // indicate that context might no longer exist - uv_close((uv_handle_t*)&p->handle, on_close); -} - - -static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) { - redisContext *c = &(ac->c); - - if (ac->ev.data != NULL) { - return REDIS_ERR; - } - - ac->ev.addRead = redisLibuvAddRead; - ac->ev.delRead = redisLibuvDelRead; - ac->ev.addWrite = redisLibuvAddWrite; - ac->ev.delWrite = redisLibuvDelWrite; - ac->ev.cleanup = redisLibuvCleanup; - - redisLibuvEvents* p = (redisLibuvEvents*)malloc(sizeof(*p)); - - if (!p) { - return REDIS_ERR; - } - - memset(p, 0, sizeof(*p)); - - if (uv_poll_init(loop, &p->handle, c->fd) != 0) { - return REDIS_ERR; - } - - ac->ev.data = p; - p->handle.data = p; - p->context = ac; - - return REDIS_OK; -} -#endif diff --git a/ext/hiredis-0.14.1/adapters/macosx.h b/ext/hiredis-0.14.1/adapters/macosx.h deleted file mode 100644 index 72121f606..000000000 --- a/ext/hiredis-0.14.1/adapters/macosx.h +++ /dev/null @@ -1,114 +0,0 @@ -// -// Created by Дмитрий Бахвалов on 13.07.15. -// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved. -// - -#ifndef __HIREDIS_MACOSX_H__ -#define __HIREDIS_MACOSX_H__ - -#include - -#include "../hiredis.h" -#include "../async.h" - -typedef struct { - redisAsyncContext *context; - CFSocketRef socketRef; - CFRunLoopSourceRef sourceRef; -} RedisRunLoop; - -static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) { - if( redisRunLoop != NULL ) { - if( redisRunLoop->sourceRef != NULL ) { - CFRunLoopSourceInvalidate(redisRunLoop->sourceRef); - CFRelease(redisRunLoop->sourceRef); - } - if( redisRunLoop->socketRef != NULL ) { - CFSocketInvalidate(redisRunLoop->socketRef); - CFRelease(redisRunLoop->socketRef); - } - free(redisRunLoop); - } - return REDIS_ERR; -} - -static void redisMacOSAddRead(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack); -} - -static void redisMacOSDelRead(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack); -} - -static void redisMacOSAddWrite(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack); -} - -static void redisMacOSDelWrite(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack); -} - -static void redisMacOSCleanup(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - freeRedisRunLoop(redisRunLoop); -} - -static void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) { - redisAsyncContext* context = (redisAsyncContext*) info; - - switch (callbackType) { - case kCFSocketReadCallBack: - redisAsyncHandleRead(context); - break; - - case kCFSocketWriteCallBack: - redisAsyncHandleWrite(context); - break; - - default: - break; - } -} - -static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) { - redisContext *redisCtx = &(redisAsyncCtx->c); - - /* Nothing should be attached when something is already attached */ - if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR; - - RedisRunLoop* redisRunLoop = (RedisRunLoop*) calloc(1, sizeof(RedisRunLoop)); - if( !redisRunLoop ) return REDIS_ERR; - - /* Setup redis stuff */ - redisRunLoop->context = redisAsyncCtx; - - redisAsyncCtx->ev.addRead = redisMacOSAddRead; - redisAsyncCtx->ev.delRead = redisMacOSDelRead; - redisAsyncCtx->ev.addWrite = redisMacOSAddWrite; - redisAsyncCtx->ev.delWrite = redisMacOSDelWrite; - redisAsyncCtx->ev.cleanup = redisMacOSCleanup; - redisAsyncCtx->ev.data = redisRunLoop; - - /* Initialize and install read/write events */ - CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL }; - - redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd, - kCFSocketReadCallBack | kCFSocketWriteCallBack, - redisMacOSAsyncCallback, - &socketCtx); - if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop); - - redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0); - if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop); - - CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode); - - return REDIS_OK; -} - -#endif - diff --git a/ext/hiredis-0.14.1/adapters/qt.h b/ext/hiredis-0.14.1/adapters/qt.h deleted file mode 100644 index 5cc02e6ce..000000000 --- a/ext/hiredis-0.14.1/adapters/qt.h +++ /dev/null @@ -1,135 +0,0 @@ -/*- - * Copyright (C) 2014 Pietro Cerutti - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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. - */ - -#ifndef __HIREDIS_QT_H__ -#define __HIREDIS_QT_H__ -#include -#include "../async.h" - -static void RedisQtAddRead(void *); -static void RedisQtDelRead(void *); -static void RedisQtAddWrite(void *); -static void RedisQtDelWrite(void *); -static void RedisQtCleanup(void *); - -class RedisQtAdapter : public QObject { - - Q_OBJECT - - friend - void RedisQtAddRead(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->addRead(); - } - - friend - void RedisQtDelRead(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->delRead(); - } - - friend - void RedisQtAddWrite(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->addWrite(); - } - - friend - void RedisQtDelWrite(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->delWrite(); - } - - friend - void RedisQtCleanup(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->cleanup(); - } - - public: - RedisQtAdapter(QObject * parent = 0) - : QObject(parent), m_ctx(0), m_read(0), m_write(0) { } - - ~RedisQtAdapter() { - if (m_ctx != 0) { - m_ctx->ev.data = NULL; - } - } - - int setContext(redisAsyncContext * ac) { - if (ac->ev.data != NULL) { - return REDIS_ERR; - } - m_ctx = ac; - m_ctx->ev.data = this; - m_ctx->ev.addRead = RedisQtAddRead; - m_ctx->ev.delRead = RedisQtDelRead; - m_ctx->ev.addWrite = RedisQtAddWrite; - m_ctx->ev.delWrite = RedisQtDelWrite; - m_ctx->ev.cleanup = RedisQtCleanup; - return REDIS_OK; - } - - private: - void addRead() { - if (m_read) return; - m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0); - connect(m_read, SIGNAL(activated(int)), this, SLOT(read())); - } - - void delRead() { - if (!m_read) return; - delete m_read; - m_read = 0; - } - - void addWrite() { - if (m_write) return; - m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0); - connect(m_write, SIGNAL(activated(int)), this, SLOT(write())); - } - - void delWrite() { - if (!m_write) return; - delete m_write; - m_write = 0; - } - - void cleanup() { - delRead(); - delWrite(); - } - - private slots: - void read() { redisAsyncHandleRead(m_ctx); } - void write() { redisAsyncHandleWrite(m_ctx); } - - private: - redisAsyncContext * m_ctx; - QSocketNotifier * m_read; - QSocketNotifier * m_write; -}; - -#endif /* !__HIREDIS_QT_H__ */ diff --git a/ext/hiredis-0.14.1/alloc.c b/ext/hiredis-0.14.1/alloc.c deleted file mode 100644 index 55c3020e7..000000000 --- a/ext/hiredis-0.14.1/alloc.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2020, Michael Grunder - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "fmacros.h" -#include "alloc.h" -#include - -void *hi_malloc(size_t size) { - void *ptr = malloc(size); - if (ptr == NULL) - HIREDIS_OOM_HANDLER; - - return ptr; -} - -void *hi_calloc(size_t nmemb, size_t size) { - void *ptr = calloc(nmemb, size); - if (ptr == NULL) - HIREDIS_OOM_HANDLER; - - return ptr; -} - -void *hi_realloc(void *ptr, size_t size) { - void *newptr = realloc(ptr, size); - if (newptr == NULL) - HIREDIS_OOM_HANDLER; - - return newptr; -} - -char *hi_strdup(const char *str) { - char *newstr = strdup(str); - if (newstr == NULL) - HIREDIS_OOM_HANDLER; - - return newstr; -} diff --git a/ext/hiredis-0.14.1/alloc.h b/ext/hiredis-0.14.1/alloc.h deleted file mode 100644 index 2c9b04e35..000000000 --- a/ext/hiredis-0.14.1/alloc.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2020, Michael Grunder - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef HIREDIS_ALLOC_H -#define HIREDIS_ALLOC_H - -#include /* for size_t */ - -#ifndef HIREDIS_OOM_HANDLER -#define HIREDIS_OOM_HANDLER abort() -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -void *hi_malloc(size_t size); -void *hi_calloc(size_t nmemb, size_t size); -void *hi_realloc(void *ptr, size_t size); -char *hi_strdup(const char *str); - -#ifdef __cplusplus -} -#endif - -#endif /* HIREDIS_ALLOC_H */ diff --git a/ext/hiredis-0.14.1/appveyor.yml b/ext/hiredis-0.14.1/appveyor.yml deleted file mode 100644 index 819efbd58..000000000 --- a/ext/hiredis-0.14.1/appveyor.yml +++ /dev/null @@ -1,23 +0,0 @@ -# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin) -environment: - matrix: - - CYG_BASH: C:\cygwin64\bin\bash - CC: gcc - - CYG_BASH: C:\cygwin\bin\bash - CC: gcc - TARGET: 32bit - TARGET_VARS: 32bit-vars - -clone_depth: 1 - -# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail -init: - - git config --global core.autocrlf input - -# Install needed build dependencies -install: - - '%CYG_BASH% -lc "cygcheck -dc cygwin"' - -build_script: - - 'echo building...' - - '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0 - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "fmacros.h" -#include "alloc.h" -#include -#include -#include -#include -#include -#include -#include "async.h" -#include "net.h" -#include "dict.c" -#include "sds.h" - -#define _EL_ADD_READ(ctx) do { \ - if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \ - } while(0) -#define _EL_DEL_READ(ctx) do { \ - if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \ - } while(0) -#define _EL_ADD_WRITE(ctx) do { \ - if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \ - } while(0) -#define _EL_DEL_WRITE(ctx) do { \ - if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \ - } while(0) -#define _EL_CLEANUP(ctx) do { \ - if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \ - } while(0); - -/* Forward declaration of function in hiredis.c */ -int __redisAppendCommand(redisContext *c, const char *cmd, size_t len); - -/* Functions managing dictionary of callbacks for pub/sub. */ -static unsigned int callbackHash(const void *key) { - return dictGenHashFunction((const unsigned char *)key, - sdslen((const sds)key)); -} - -static void *callbackValDup(void *privdata, const void *src) { - ((void) privdata); - redisCallback *dup = hi_malloc(sizeof(*dup)); - memcpy(dup,src,sizeof(*dup)); - return dup; -} - -static int callbackKeyCompare(void *privdata, const void *key1, const void *key2) { - int l1, l2; - ((void) privdata); - - l1 = sdslen((const sds)key1); - l2 = sdslen((const sds)key2); - if (l1 != l2) return 0; - return memcmp(key1,key2,l1) == 0; -} - -static void callbackKeyDestructor(void *privdata, void *key) { - ((void) privdata); - sdsfree((sds)key); -} - -static void callbackValDestructor(void *privdata, void *val) { - ((void) privdata); - free(val); -} - -static dictType callbackDict = { - callbackHash, - NULL, - callbackValDup, - callbackKeyCompare, - callbackKeyDestructor, - callbackValDestructor -}; - -static redisAsyncContext *redisAsyncInitialize(redisContext *c) { - redisAsyncContext *ac; - - ac = realloc(c,sizeof(redisAsyncContext)); - if (ac == NULL) - return NULL; - - c = &(ac->c); - - /* The regular connect functions will always set the flag REDIS_CONNECTED. - * For the async API, we want to wait until the first write event is - * received up before setting this flag, so reset it here. */ - c->flags &= ~REDIS_CONNECTED; - - ac->err = 0; - ac->errstr = NULL; - ac->data = NULL; - - ac->ev.data = NULL; - ac->ev.addRead = NULL; - ac->ev.delRead = NULL; - ac->ev.addWrite = NULL; - ac->ev.delWrite = NULL; - ac->ev.cleanup = NULL; - - ac->onConnect = NULL; - ac->onDisconnect = NULL; - - ac->replies.head = NULL; - ac->replies.tail = NULL; - ac->sub.invalid.head = NULL; - ac->sub.invalid.tail = NULL; - ac->sub.channels = dictCreate(&callbackDict,NULL); - ac->sub.patterns = dictCreate(&callbackDict,NULL); - return ac; -} - -/* We want the error field to be accessible directly instead of requiring - * an indirection to the redisContext struct. */ -static void __redisAsyncCopyError(redisAsyncContext *ac) { - if (!ac) - return; - - redisContext *c = &(ac->c); - ac->err = c->err; - ac->errstr = c->errstr; -} - -redisAsyncContext *redisAsyncConnect(const char *ip, int port) { - redisContext *c; - redisAsyncContext *ac; - - c = redisConnectNonBlock(ip,port); - if (c == NULL) - return NULL; - - ac = redisAsyncInitialize(c); - if (ac == NULL) { - redisFree(c); - return NULL; - } - - __redisAsyncCopyError(ac); - return ac; -} - -redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, - const char *source_addr) { - redisContext *c = redisConnectBindNonBlock(ip,port,source_addr); - redisAsyncContext *ac = redisAsyncInitialize(c); - __redisAsyncCopyError(ac); - return ac; -} - -redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, - const char *source_addr) { - redisContext *c = redisConnectBindNonBlockWithReuse(ip,port,source_addr); - redisAsyncContext *ac = redisAsyncInitialize(c); - __redisAsyncCopyError(ac); - return ac; -} - -redisAsyncContext *redisAsyncConnectUnix(const char *path) { - redisContext *c; - redisAsyncContext *ac; - - c = redisConnectUnixNonBlock(path); - if (c == NULL) - return NULL; - - ac = redisAsyncInitialize(c); - if (ac == NULL) { - redisFree(c); - return NULL; - } - - __redisAsyncCopyError(ac); - return ac; -} - -int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) { - if (ac->onConnect == NULL) { - ac->onConnect = fn; - - /* The common way to detect an established connection is to wait for - * the first write event to be fired. This assumes the related event - * library functions are already set. */ - _EL_ADD_WRITE(ac); - return REDIS_OK; - } - return REDIS_ERR; -} - -int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) { - if (ac->onDisconnect == NULL) { - ac->onDisconnect = fn; - return REDIS_OK; - } - return REDIS_ERR; -} - -/* Helper functions to push/shift callbacks */ -static int __redisPushCallback(redisCallbackList *list, redisCallback *source) { - redisCallback *cb; - - /* Copy callback from stack to heap */ - cb = malloc(sizeof(*cb)); - if (cb == NULL) - return REDIS_ERR_OOM; - - if (source != NULL) { - memcpy(cb,source,sizeof(*cb)); - cb->next = NULL; - } - - /* Store callback in list */ - if (list->head == NULL) - list->head = cb; - if (list->tail != NULL) - list->tail->next = cb; - list->tail = cb; - return REDIS_OK; -} - -static int __redisShiftCallback(redisCallbackList *list, redisCallback *target) { - redisCallback *cb = list->head; - if (cb != NULL) { - list->head = cb->next; - if (cb == list->tail) - list->tail = NULL; - - /* Copy callback from heap to stack */ - if (target != NULL) - memcpy(target,cb,sizeof(*cb)); - free(cb); - return REDIS_OK; - } - return REDIS_ERR; -} - -static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) { - redisContext *c = &(ac->c); - if (cb->fn != NULL) { - c->flags |= REDIS_IN_CALLBACK; - cb->fn(ac,reply,cb->privdata); - c->flags &= ~REDIS_IN_CALLBACK; - } -} - -/* Helper function to free the context. */ -static void __redisAsyncFree(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisCallback cb; - dictIterator *it; - dictEntry *de; - - /* Execute pending callbacks with NULL reply. */ - while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK) - __redisRunCallback(ac,&cb,NULL); - - /* Execute callbacks for invalid commands */ - while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK) - __redisRunCallback(ac,&cb,NULL); - - /* Run subscription callbacks callbacks with NULL reply */ - it = dictGetIterator(ac->sub.channels); - while ((de = dictNext(it)) != NULL) - __redisRunCallback(ac,dictGetEntryVal(de),NULL); - dictReleaseIterator(it); - dictRelease(ac->sub.channels); - - it = dictGetIterator(ac->sub.patterns); - while ((de = dictNext(it)) != NULL) - __redisRunCallback(ac,dictGetEntryVal(de),NULL); - dictReleaseIterator(it); - dictRelease(ac->sub.patterns); - - /* Signal event lib to clean up */ - _EL_CLEANUP(ac); - - /* Execute disconnect callback. When redisAsyncFree() initiated destroying - * this context, the status will always be REDIS_OK. */ - if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) { - if (c->flags & REDIS_FREEING) { - ac->onDisconnect(ac,REDIS_OK); - } else { - ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR); - } - } - - /* Cleanup self */ - redisFree(c); -} - -/* Free the async context. When this function is called from a callback, - * control needs to be returned to redisProcessCallbacks() before actual - * free'ing. To do so, a flag is set on the context which is picked up by - * redisProcessCallbacks(). Otherwise, the context is immediately free'd. */ -void redisAsyncFree(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - c->flags |= REDIS_FREEING; - if (!(c->flags & REDIS_IN_CALLBACK)) - __redisAsyncFree(ac); -} - -/* Helper function to make the disconnect happen and clean up. */ -static void __redisAsyncDisconnect(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - - /* Make sure error is accessible if there is any */ - __redisAsyncCopyError(ac); - - if (ac->err == 0) { - /* For clean disconnects, there should be no pending callbacks. */ - int ret = __redisShiftCallback(&ac->replies,NULL); - assert(ret == REDIS_ERR); - } else { - /* Disconnection is caused by an error, make sure that pending - * callbacks cannot call new commands. */ - c->flags |= REDIS_DISCONNECTING; - } - - /* For non-clean disconnects, __redisAsyncFree() will execute pending - * callbacks with a NULL-reply. */ - __redisAsyncFree(ac); -} - -/* Tries to do a clean disconnect from Redis, meaning it stops new commands - * from being issued, but tries to flush the output buffer and execute - * callbacks for all remaining replies. When this function is called from a - * callback, there might be more replies and we can safely defer disconnecting - * to redisProcessCallbacks(). Otherwise, we can only disconnect immediately - * when there are no pending callbacks. */ -void redisAsyncDisconnect(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - c->flags |= REDIS_DISCONNECTING; - if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL) - __redisAsyncDisconnect(ac); -} - -static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) { - redisContext *c = &(ac->c); - dict *callbacks; - redisCallback *cb; - dictEntry *de; - int pvariant; - char *stype; - sds sname; - - /* Custom reply functions are not supported for pub/sub. This will fail - * very hard when they are used... */ - if (reply->type == REDIS_REPLY_ARRAY) { - assert(reply->elements >= 2); - assert(reply->element[0]->type == REDIS_REPLY_STRING); - stype = reply->element[0]->str; - pvariant = (tolower(stype[0]) == 'p') ? 1 : 0; - - if (pvariant) - callbacks = ac->sub.patterns; - else - callbacks = ac->sub.channels; - - /* Locate the right callback */ - assert(reply->element[1]->type == REDIS_REPLY_STRING); - sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len); - de = dictFind(callbacks,sname); - if (de != NULL) { - cb = dictGetEntryVal(de); - - /* If this is an subscribe reply decrease pending counter. */ - if (strcasecmp(stype+pvariant,"subscribe") == 0) { - cb->pending_subs -= 1; - } - - memcpy(dstcb,cb,sizeof(*dstcb)); - - /* If this is an unsubscribe message, remove it. */ - if (strcasecmp(stype+pvariant,"unsubscribe") == 0) { - if (cb->pending_subs == 0) - dictDelete(callbacks,sname); - - /* If this was the last unsubscribe message, revert to - * non-subscribe mode. */ - assert(reply->element[2]->type == REDIS_REPLY_INTEGER); - - /* Unset subscribed flag only when no pipelined pending subscribe. */ - if (reply->element[2]->integer == 0 - && dictSize(ac->sub.channels) == 0 - && dictSize(ac->sub.patterns) == 0) - c->flags &= ~REDIS_SUBSCRIBED; - } - } - sdsfree(sname); - } else { - /* Shift callback for invalid commands. */ - __redisShiftCallback(&ac->sub.invalid,dstcb); - } - return REDIS_OK; -} - -void redisProcessCallbacks(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisCallback cb = {NULL, NULL, 0, NULL}; - void *reply = NULL; - int status; - - while((status = redisGetReply(c,&reply)) == REDIS_OK) { - if (reply == NULL) { - /* When the connection is being disconnected and there are - * no more replies, this is the cue to really disconnect. */ - if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0 - && ac->replies.head == NULL) { - __redisAsyncDisconnect(ac); - return; - } - - /* If monitor mode, repush callback */ - if(c->flags & REDIS_MONITORING) { - __redisPushCallback(&ac->replies,&cb); - } - - /* When the connection is not being disconnected, simply stop - * trying to get replies and wait for the next loop tick. */ - break; - } - - /* Even if the context is subscribed, pending regular callbacks will - * get a reply before pub/sub messages arrive. */ - if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) { - /* - * A spontaneous reply in a not-subscribed context can be the error - * reply that is sent when a new connection exceeds the maximum - * number of allowed connections on the server side. - * - * This is seen as an error instead of a regular reply because the - * server closes the connection after sending it. - * - * To prevent the error from being overwritten by an EOF error the - * connection is closed here. See issue #43. - * - * Another possibility is that the server is loading its dataset. - * In this case we also want to close the connection, and have the - * user wait until the server is ready to take our request. - */ - if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) { - c->err = REDIS_ERR_OTHER; - snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str); - c->reader->fn->freeObject(reply); - __redisAsyncDisconnect(ac); - return; - } - /* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */ - assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING)); - if(c->flags & REDIS_SUBSCRIBED) - __redisGetSubscribeCallback(ac,reply,&cb); - } - - if (cb.fn != NULL) { - __redisRunCallback(ac,&cb,reply); - c->reader->fn->freeObject(reply); - - /* Proceed with free'ing when redisAsyncFree() was called. */ - if (c->flags & REDIS_FREEING) { - __redisAsyncFree(ac); - return; - } - } else { - /* No callback for this reply. This can either be a NULL callback, - * or there were no callbacks to begin with. Either way, don't - * abort with an error, but simply ignore it because the client - * doesn't know what the server will spit out over the wire. */ - c->reader->fn->freeObject(reply); - } - } - - /* Disconnect when there was an error reading the reply */ - if (status != REDIS_OK) - __redisAsyncDisconnect(ac); -} - -/* Internal helper function to detect socket status the first time a read or - * write event fires. When connecting was not successful, the connect callback - * is called with a REDIS_ERR status and the context is free'd. */ -static int __redisAsyncHandleConnect(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - - if (redisCheckSocketError(c) == REDIS_ERR) { - /* Try again later when connect(2) is still in progress. */ - if (errno == EINPROGRESS) - return REDIS_OK; - - if (ac->onConnect) ac->onConnect(ac,REDIS_ERR); - __redisAsyncDisconnect(ac); - return REDIS_ERR; - } - - /* Mark context as connected. */ - c->flags |= REDIS_CONNECTED; - if (ac->onConnect) ac->onConnect(ac,REDIS_OK); - return REDIS_OK; -} - -/* This function should be called when the socket is readable. - * It processes all replies that can be read and executes their callbacks. - */ -void redisAsyncHandleRead(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - - if (!(c->flags & REDIS_CONNECTED)) { - /* Abort connect was not successful. */ - if (__redisAsyncHandleConnect(ac) != REDIS_OK) - return; - /* Try again later when the context is still not connected. */ - if (!(c->flags & REDIS_CONNECTED)) - return; - } - - if (redisBufferRead(c) == REDIS_ERR) { - __redisAsyncDisconnect(ac); - } else { - /* Always re-schedule reads */ - _EL_ADD_READ(ac); - redisProcessCallbacks(ac); - } -} - -void redisAsyncHandleWrite(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - int done = 0; - - if (!(c->flags & REDIS_CONNECTED)) { - /* Abort connect was not successful. */ - if (__redisAsyncHandleConnect(ac) != REDIS_OK) - return; - /* Try again later when the context is still not connected. */ - if (!(c->flags & REDIS_CONNECTED)) - return; - } - - if (redisBufferWrite(c,&done) == REDIS_ERR) { - __redisAsyncDisconnect(ac); - } else { - /* Continue writing when not done, stop writing otherwise */ - if (!done) - _EL_ADD_WRITE(ac); - else - _EL_DEL_WRITE(ac); - - /* Always schedule reads after writes */ - _EL_ADD_READ(ac); - } -} - -/* Sets a pointer to the first argument and its length starting at p. Returns - * the number of bytes to skip to get to the following argument. */ -static const char *nextArgument(const char *start, const char **str, size_t *len) { - const char *p = start; - if (p[0] != '$') { - p = strchr(p,'$'); - if (p == NULL) return NULL; - } - - *len = (int)strtol(p+1,NULL,10); - p = strchr(p,'\r'); - assert(p); - *str = p+2; - return p+2+(*len)+2; -} - -/* Helper function for the redisAsyncCommand* family of functions. Writes a - * formatted command to the output buffer and registers the provided callback - * function with the context. */ -static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) { - redisContext *c = &(ac->c); - redisCallback cb; - struct dict *cbdict; - dictEntry *de; - redisCallback *existcb; - int pvariant, hasnext; - const char *cstr, *astr; - size_t clen, alen; - const char *p; - sds sname; - int ret; - - /* Don't accept new commands when the connection is about to be closed. */ - if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR; - - /* Setup callback */ - cb.fn = fn; - cb.privdata = privdata; - cb.pending_subs = 1; - - /* Find out which command will be appended. */ - p = nextArgument(cmd,&cstr,&clen); - assert(p != NULL); - hasnext = (p[0] == '$'); - pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0; - cstr += pvariant; - clen -= pvariant; - - if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) { - c->flags |= REDIS_SUBSCRIBED; - - /* Add every channel/pattern to the list of subscription callbacks. */ - while ((p = nextArgument(p,&astr,&alen)) != NULL) { - sname = sdsnewlen(astr,alen); - if (pvariant) - cbdict = ac->sub.patterns; - else - cbdict = ac->sub.channels; - - de = dictFind(cbdict,sname); - - if (de != NULL) { - existcb = dictGetEntryVal(de); - cb.pending_subs = existcb->pending_subs + 1; - } - - ret = dictReplace(cbdict,sname,&cb); - - if (ret == 0) sdsfree(sname); - } - } else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) { - /* It is only useful to call (P)UNSUBSCRIBE when the context is - * subscribed to one or more channels or patterns. */ - if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR; - - /* (P)UNSUBSCRIBE does not have its own response: every channel or - * pattern that is unsubscribed will receive a message. This means we - * should not append a callback function for this command. */ - } else if(strncasecmp(cstr,"monitor\r\n",9) == 0) { - /* Set monitor flag and push callback */ - c->flags |= REDIS_MONITORING; - __redisPushCallback(&ac->replies,&cb); - } else { - if (c->flags & REDIS_SUBSCRIBED) - /* This will likely result in an error reply, but it needs to be - * received and passed to the callback. */ - __redisPushCallback(&ac->sub.invalid,&cb); - else - __redisPushCallback(&ac->replies,&cb); - } - - __redisAppendCommand(c,cmd,len); - - /* Always schedule a write when the write buffer is non-empty */ - _EL_ADD_WRITE(ac); - - return REDIS_OK; -} - -int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) { - char *cmd; - int len; - int status; - len = redisvFormatCommand(&cmd,format,ap); - - /* We don't want to pass -1 or -2 to future functions as a length. */ - if (len < 0) - return REDIS_ERR; - - status = __redisAsyncCommand(ac,fn,privdata,cmd,len); - free(cmd); - return status; -} - -int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) { - va_list ap; - int status; - va_start(ap,format); - status = redisvAsyncCommand(ac,fn,privdata,format,ap); - va_end(ap); - return status; -} - -int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) { - sds cmd; - int len; - int status; - len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen); - if (len < 0) - return REDIS_ERR; - status = __redisAsyncCommand(ac,fn,privdata,cmd,len); - sdsfree(cmd); - return status; -} - -int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) { - int status = __redisAsyncCommand(ac,fn,privdata,cmd,len); - return status; -} diff --git a/ext/hiredis-0.14.1/async.h b/ext/hiredis-0.14.1/async.h deleted file mode 100644 index e69d84090..000000000 --- a/ext/hiredis-0.14.1/async.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_ASYNC_H -#define __HIREDIS_ASYNC_H -#include "hiredis.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct redisAsyncContext; /* need forward declaration of redisAsyncContext */ -struct dict; /* dictionary header is included in async.c */ - -/* Reply callback prototype and container */ -typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*); -typedef struct redisCallback { - struct redisCallback *next; /* simple singly linked list */ - redisCallbackFn *fn; - int pending_subs; - void *privdata; -} redisCallback; - -/* List of callbacks for either regular replies or pub/sub */ -typedef struct redisCallbackList { - redisCallback *head, *tail; -} redisCallbackList; - -/* Connection callback prototypes */ -typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status); -typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status); - -/* Context for an async connection to Redis */ -typedef struct redisAsyncContext { - /* Hold the regular context, so it can be realloc'ed. */ - redisContext c; - - /* Setup error flags so they can be used directly. */ - int err; - char *errstr; - - /* Not used by hiredis */ - void *data; - - /* Event library data and hooks */ - struct { - void *data; - - /* Hooks that are called when the library expects to start - * reading/writing. These functions should be idempotent. */ - void (*addRead)(void *privdata); - void (*delRead)(void *privdata); - void (*addWrite)(void *privdata); - void (*delWrite)(void *privdata); - void (*cleanup)(void *privdata); - } ev; - - /* Called when either the connection is terminated due to an error or per - * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */ - redisDisconnectCallback *onDisconnect; - - /* Called when the first write event was received. */ - redisConnectCallback *onConnect; - - /* Regular command callbacks */ - redisCallbackList replies; - - /* Subscription callbacks */ - struct { - redisCallbackList invalid; - struct dict *channels; - struct dict *patterns; - } sub; -} redisAsyncContext; - -/* Functions that proxy to hiredis */ -redisAsyncContext *redisAsyncConnect(const char *ip, int port); -redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr); -redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, - const char *source_addr); -redisAsyncContext *redisAsyncConnectUnix(const char *path); -int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); -int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); -void redisAsyncDisconnect(redisAsyncContext *ac); -void redisAsyncFree(redisAsyncContext *ac); - -/* Handle read/write events */ -void redisAsyncHandleRead(redisAsyncContext *ac); -void redisAsyncHandleWrite(redisAsyncContext *ac); - -/* Command functions for an async context. Write the command to the - * output buffer and register the provided callback. */ -int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap); -int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...); -int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen); -int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/ext/hiredis-0.14.1/dict.c b/ext/hiredis-0.14.1/dict.c deleted file mode 100644 index 29cdc190f..000000000 --- a/ext/hiredis-0.14.1/dict.c +++ /dev/null @@ -1,339 +0,0 @@ -/* Hash table implementation. - * - * This file implements in memory hash tables with insert/del/replace/find/ - * get-random-element operations. Hash tables will auto resize if needed - * tables of power of two in size are used, collisions are handled by - * chaining. See the source code for more information... :) - * - * Copyright (c) 2006-2010, Salvatore Sanfilippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "fmacros.h" -#include "alloc.h" -#include -#include -#include -#include "dict.h" - -/* -------------------------- private prototypes ---------------------------- */ - -static int _dictExpandIfNeeded(dict *ht); -static unsigned long _dictNextPower(unsigned long size); -static int _dictKeyIndex(dict *ht, const void *key); -static int _dictInit(dict *ht, dictType *type, void *privDataPtr); - -/* -------------------------- hash functions -------------------------------- */ - -/* Generic hash function (a popular one from Bernstein). - * I tested a few and this was the best. */ -static unsigned int dictGenHashFunction(const unsigned char *buf, int len) { - unsigned int hash = 5381; - - while (len--) - hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */ - return hash; -} - -/* ----------------------------- API implementation ------------------------- */ - -/* Reset an hashtable already initialized with ht_init(). - * NOTE: This function should only called by ht_destroy(). */ -static void _dictReset(dict *ht) { - ht->table = NULL; - ht->size = 0; - ht->sizemask = 0; - ht->used = 0; -} - -/* Create a new hash table */ -static dict *dictCreate(dictType *type, void *privDataPtr) { - dict *ht = hi_malloc(sizeof(*ht)); - _dictInit(ht,type,privDataPtr); - return ht; -} - -/* Initialize the hash table */ -static int _dictInit(dict *ht, dictType *type, void *privDataPtr) { - _dictReset(ht); - ht->type = type; - ht->privdata = privDataPtr; - return DICT_OK; -} - -/* Expand or create the hashtable */ -static int dictExpand(dict *ht, unsigned long size) { - dict n; /* the new hashtable */ - unsigned long realsize = _dictNextPower(size), i; - - /* the size is invalid if it is smaller than the number of - * elements already inside the hashtable */ - if (ht->used > size) - return DICT_ERR; - - _dictInit(&n, ht->type, ht->privdata); - n.size = realsize; - n.sizemask = realsize-1; - n.table = calloc(realsize,sizeof(dictEntry*)); - - /* Copy all the elements from the old to the new table: - * note that if the old hash table is empty ht->size is zero, - * so dictExpand just creates an hash table. */ - n.used = ht->used; - for (i = 0; i < ht->size && ht->used > 0; i++) { - dictEntry *he, *nextHe; - - if (ht->table[i] == NULL) continue; - - /* For each hash entry on this slot... */ - he = ht->table[i]; - while(he) { - unsigned int h; - - nextHe = he->next; - /* Get the new element index */ - h = dictHashKey(ht, he->key) & n.sizemask; - he->next = n.table[h]; - n.table[h] = he; - ht->used--; - /* Pass to the next element */ - he = nextHe; - } - } - assert(ht->used == 0); - free(ht->table); - - /* Remap the new hashtable in the old */ - *ht = n; - return DICT_OK; -} - -/* Add an element to the target hash table */ -static int dictAdd(dict *ht, void *key, void *val) { - int index; - dictEntry *entry; - - /* Get the index of the new element, or -1 if - * the element already exists. */ - if ((index = _dictKeyIndex(ht, key)) == -1) - return DICT_ERR; - - /* Allocates the memory and stores key */ - entry = hi_malloc(sizeof(*entry)); - entry->next = ht->table[index]; - ht->table[index] = entry; - - /* Set the hash entry fields. */ - dictSetHashKey(ht, entry, key); - dictSetHashVal(ht, entry, val); - ht->used++; - return DICT_OK; -} - -/* Add an element, discarding the old if the key already exists. - * Return 1 if the key was added from scratch, 0 if there was already an - * element with such key and dictReplace() just performed a value update - * operation. */ -static int dictReplace(dict *ht, void *key, void *val) { - dictEntry *entry, auxentry; - - /* Try to add the element. If the key - * does not exists dictAdd will succeed. */ - if (dictAdd(ht, key, val) == DICT_OK) - return 1; - /* It already exists, get the entry */ - entry = dictFind(ht, key); - /* Free the old value and set the new one */ - /* Set the new value and free the old one. Note that it is important - * to do that in this order, as the value may just be exactly the same - * as the previous one. In this context, think to reference counting, - * you want to increment (set), and then decrement (free), and not the - * reverse. */ - auxentry = *entry; - dictSetHashVal(ht, entry, val); - dictFreeEntryVal(ht, &auxentry); - return 0; -} - -/* Search and remove an element */ -static int dictDelete(dict *ht, const void *key) { - unsigned int h; - dictEntry *de, *prevde; - - if (ht->size == 0) - return DICT_ERR; - h = dictHashKey(ht, key) & ht->sizemask; - de = ht->table[h]; - - prevde = NULL; - while(de) { - if (dictCompareHashKeys(ht,key,de->key)) { - /* Unlink the element from the list */ - if (prevde) - prevde->next = de->next; - else - ht->table[h] = de->next; - - dictFreeEntryKey(ht,de); - dictFreeEntryVal(ht,de); - free(de); - ht->used--; - return DICT_OK; - } - prevde = de; - de = de->next; - } - return DICT_ERR; /* not found */ -} - -/* Destroy an entire hash table */ -static int _dictClear(dict *ht) { - unsigned long i; - - /* Free all the elements */ - for (i = 0; i < ht->size && ht->used > 0; i++) { - dictEntry *he, *nextHe; - - if ((he = ht->table[i]) == NULL) continue; - while(he) { - nextHe = he->next; - dictFreeEntryKey(ht, he); - dictFreeEntryVal(ht, he); - free(he); - ht->used--; - he = nextHe; - } - } - /* Free the table and the allocated cache structure */ - free(ht->table); - /* Re-initialize the table */ - _dictReset(ht); - return DICT_OK; /* never fails */ -} - -/* Clear & Release the hash table */ -static void dictRelease(dict *ht) { - _dictClear(ht); - free(ht); -} - -static dictEntry *dictFind(dict *ht, const void *key) { - dictEntry *he; - unsigned int h; - - if (ht->size == 0) return NULL; - h = dictHashKey(ht, key) & ht->sizemask; - he = ht->table[h]; - while(he) { - if (dictCompareHashKeys(ht, key, he->key)) - return he; - he = he->next; - } - return NULL; -} - -static dictIterator *dictGetIterator(dict *ht) { - dictIterator *iter = hi_malloc(sizeof(*iter)); - - iter->ht = ht; - iter->index = -1; - iter->entry = NULL; - iter->nextEntry = NULL; - return iter; -} - -static dictEntry *dictNext(dictIterator *iter) { - while (1) { - if (iter->entry == NULL) { - iter->index++; - if (iter->index >= - (signed)iter->ht->size) break; - iter->entry = iter->ht->table[iter->index]; - } else { - iter->entry = iter->nextEntry; - } - if (iter->entry) { - /* We need to save the 'next' here, the iterator user - * may delete the entry we are returning. */ - iter->nextEntry = iter->entry->next; - return iter->entry; - } - } - return NULL; -} - -static void dictReleaseIterator(dictIterator *iter) { - free(iter); -} - -/* ------------------------- private functions ------------------------------ */ - -/* Expand the hash table if needed */ -static int _dictExpandIfNeeded(dict *ht) { - /* If the hash table is empty expand it to the initial size, - * if the table is "full" dobule its size. */ - if (ht->size == 0) - return dictExpand(ht, DICT_HT_INITIAL_SIZE); - if (ht->used == ht->size) - return dictExpand(ht, ht->size*2); - return DICT_OK; -} - -/* Our hash table capability is a power of two */ -static unsigned long _dictNextPower(unsigned long size) { - unsigned long i = DICT_HT_INITIAL_SIZE; - - if (size >= LONG_MAX) return LONG_MAX; - while(1) { - if (i >= size) - return i; - i *= 2; - } -} - -/* Returns the index of a free slot that can be populated with - * an hash entry for the given 'key'. - * If the key already exists, -1 is returned. */ -static int _dictKeyIndex(dict *ht, const void *key) { - unsigned int h; - dictEntry *he; - - /* Expand the hashtable if needed */ - if (_dictExpandIfNeeded(ht) == DICT_ERR) - return -1; - /* Compute the key hash value */ - h = dictHashKey(ht, key) & ht->sizemask; - /* Search if this slot does not already contain the given key */ - he = ht->table[h]; - while(he) { - if (dictCompareHashKeys(ht, key, he->key)) - return -1; - he = he->next; - } - return h; -} - diff --git a/ext/hiredis-0.14.1/dict.h b/ext/hiredis-0.14.1/dict.h deleted file mode 100644 index 95fcd280e..000000000 --- a/ext/hiredis-0.14.1/dict.h +++ /dev/null @@ -1,126 +0,0 @@ -/* Hash table implementation. - * - * This file implements in memory hash tables with insert/del/replace/find/ - * get-random-element operations. Hash tables will auto resize if needed - * tables of power of two in size are used, collisions are handled by - * chaining. See the source code for more information... :) - * - * Copyright (c) 2006-2010, Salvatore Sanfilippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __DICT_H -#define __DICT_H - -#define DICT_OK 0 -#define DICT_ERR 1 - -/* Unused arguments generate annoying warnings... */ -#define DICT_NOTUSED(V) ((void) V) - -typedef struct dictEntry { - void *key; - void *val; - struct dictEntry *next; -} dictEntry; - -typedef struct dictType { - unsigned int (*hashFunction)(const void *key); - void *(*keyDup)(void *privdata, const void *key); - void *(*valDup)(void *privdata, const void *obj); - int (*keyCompare)(void *privdata, const void *key1, const void *key2); - void (*keyDestructor)(void *privdata, void *key); - void (*valDestructor)(void *privdata, void *obj); -} dictType; - -typedef struct dict { - dictEntry **table; - dictType *type; - unsigned long size; - unsigned long sizemask; - unsigned long used; - void *privdata; -} dict; - -typedef struct dictIterator { - dict *ht; - int index; - dictEntry *entry, *nextEntry; -} dictIterator; - -/* This is the initial size of every hash table */ -#define DICT_HT_INITIAL_SIZE 4 - -/* ------------------------------- Macros ------------------------------------*/ -#define dictFreeEntryVal(ht, entry) \ - if ((ht)->type->valDestructor) \ - (ht)->type->valDestructor((ht)->privdata, (entry)->val) - -#define dictSetHashVal(ht, entry, _val_) do { \ - if ((ht)->type->valDup) \ - entry->val = (ht)->type->valDup((ht)->privdata, _val_); \ - else \ - entry->val = (_val_); \ -} while(0) - -#define dictFreeEntryKey(ht, entry) \ - if ((ht)->type->keyDestructor) \ - (ht)->type->keyDestructor((ht)->privdata, (entry)->key) - -#define dictSetHashKey(ht, entry, _key_) do { \ - if ((ht)->type->keyDup) \ - entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \ - else \ - entry->key = (_key_); \ -} while(0) - -#define dictCompareHashKeys(ht, key1, key2) \ - (((ht)->type->keyCompare) ? \ - (ht)->type->keyCompare((ht)->privdata, key1, key2) : \ - (key1) == (key2)) - -#define dictHashKey(ht, key) (ht)->type->hashFunction(key) - -#define dictGetEntryKey(he) ((he)->key) -#define dictGetEntryVal(he) ((he)->val) -#define dictSlots(ht) ((ht)->size) -#define dictSize(ht) ((ht)->used) - -/* API */ -static unsigned int dictGenHashFunction(const unsigned char *buf, int len); -static dict *dictCreate(dictType *type, void *privDataPtr); -static int dictExpand(dict *ht, unsigned long size); -static int dictAdd(dict *ht, void *key, void *val); -static int dictReplace(dict *ht, void *key, void *val); -static int dictDelete(dict *ht, const void *key); -static void dictRelease(dict *ht); -static dictEntry * dictFind(dict *ht, const void *key); -static dictIterator *dictGetIterator(dict *ht); -static dictEntry *dictNext(dictIterator *iter); -static void dictReleaseIterator(dictIterator *iter); - -#endif /* __DICT_H */ diff --git a/ext/hiredis-0.14.1/examples/example-ae.c b/ext/hiredis-0.14.1/examples/example-ae.c deleted file mode 100644 index 8efa7306a..000000000 --- a/ext/hiredis-0.14.1/examples/example-ae.c +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -/* Put event loop in the global scope, so it can be explicitly stopped */ -static aeEventLoop *loop; - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - aeStop(loop); - return; - } - - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - aeStop(loop); - return; - } - - printf("Disconnected...\n"); - aeStop(loop); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - loop = aeCreateEventLoop(64); - redisAeAttach(loop, c); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - aeMain(loop); - return 0; -} - diff --git a/ext/hiredis-0.14.1/examples/example-glib.c b/ext/hiredis-0.14.1/examples/example-glib.c deleted file mode 100644 index d6e10f8e8..000000000 --- a/ext/hiredis-0.14.1/examples/example-glib.c +++ /dev/null @@ -1,73 +0,0 @@ -#include - -#include -#include -#include - -static GMainLoop *mainloop; - -static void -connect_cb (const redisAsyncContext *ac G_GNUC_UNUSED, - int status) -{ - if (status != REDIS_OK) { - g_printerr("Failed to connect: %s\n", ac->errstr); - g_main_loop_quit(mainloop); - } else { - g_printerr("Connected...\n"); - } -} - -static void -disconnect_cb (const redisAsyncContext *ac G_GNUC_UNUSED, - int status) -{ - if (status != REDIS_OK) { - g_error("Failed to disconnect: %s", ac->errstr); - } else { - g_printerr("Disconnected...\n"); - g_main_loop_quit(mainloop); - } -} - -static void -command_cb(redisAsyncContext *ac, - gpointer r, - gpointer user_data G_GNUC_UNUSED) -{ - redisReply *reply = r; - - if (reply) { - g_print("REPLY: %s\n", reply->str); - } - - redisAsyncDisconnect(ac); -} - -gint -main (gint argc G_GNUC_UNUSED, - gchar *argv[] G_GNUC_UNUSED) -{ - redisAsyncContext *ac; - GMainContext *context = NULL; - GSource *source; - - ac = redisAsyncConnect("127.0.0.1", 6379); - if (ac->err) { - g_printerr("%s\n", ac->errstr); - exit(EXIT_FAILURE); - } - - source = redis_source_new(ac); - mainloop = g_main_loop_new(context, FALSE); - g_source_attach(source, context); - - redisAsyncSetConnectCallback(ac, connect_cb); - redisAsyncSetDisconnectCallback(ac, disconnect_cb); - redisAsyncCommand(ac, command_cb, NULL, "SET key 1234"); - redisAsyncCommand(ac, command_cb, NULL, "GET key"); - - g_main_loop_run(mainloop); - - return EXIT_SUCCESS; -} diff --git a/ext/hiredis-0.14.1/examples/example-ivykis.c b/ext/hiredis-0.14.1/examples/example-ivykis.c deleted file mode 100644 index 67affcef3..000000000 --- a/ext/hiredis-0.14.1/examples/example-ivykis.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - - iv_init(); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisIvykisAttach(c); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - - iv_main(); - - iv_deinit(); - - return 0; -} diff --git a/ext/hiredis-0.14.1/examples/example-libev.c b/ext/hiredis-0.14.1/examples/example-libev.c deleted file mode 100644 index cc8b166ec..000000000 --- a/ext/hiredis-0.14.1/examples/example-libev.c +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisLibevAttach(EV_DEFAULT_ c); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - ev_loop(EV_DEFAULT_ 0); - return 0; -} diff --git a/ext/hiredis-0.14.1/examples/example-libevent.c b/ext/hiredis-0.14.1/examples/example-libevent.c deleted file mode 100644 index d333c22b7..000000000 --- a/ext/hiredis-0.14.1/examples/example-libevent.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - struct event_base *base = event_base_new(); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisLibeventAttach(c,base); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - event_base_dispatch(base); - return 0; -} diff --git a/ext/hiredis-0.14.1/examples/example-libuv.c b/ext/hiredis-0.14.1/examples/example-libuv.c deleted file mode 100644 index a5462d410..000000000 --- a/ext/hiredis-0.14.1/examples/example-libuv.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - uv_loop_t* loop = uv_default_loop(); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisLibuvAttach(c,loop); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - uv_run(loop, UV_RUN_DEFAULT); - return 0; -} diff --git a/ext/hiredis-0.14.1/examples/example-macosx.c b/ext/hiredis-0.14.1/examples/example-macosx.c deleted file mode 100644 index bc84ed5ba..000000000 --- a/ext/hiredis-0.14.1/examples/example-macosx.c +++ /dev/null @@ -1,66 +0,0 @@ -// -// Created by Дмитрий Бахвалов on 13.07.15. -// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved. -// - -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - CFRunLoopStop(CFRunLoopGetCurrent()); - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - - CFRunLoopRef loop = CFRunLoopGetCurrent(); - if( !loop ) { - printf("Error: Cannot get current run loop\n"); - return 1; - } - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisMacOSAttach(c, loop); - - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - - CFRunLoopRun(); - - return 0; -} - diff --git a/ext/hiredis-0.14.1/examples/example-qt.cpp b/ext/hiredis-0.14.1/examples/example-qt.cpp deleted file mode 100644 index f524c3f3d..000000000 --- a/ext/hiredis-0.14.1/examples/example-qt.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include -using namespace std; - -#include -#include - -#include "example-qt.h" - -void getCallback(redisAsyncContext *, void * r, void * privdata) { - - redisReply * reply = static_cast(r); - ExampleQt * ex = static_cast(privdata); - if (reply == nullptr || ex == nullptr) return; - - cout << "key: " << reply->str << endl; - - ex->finish(); -} - -void ExampleQt::run() { - - m_ctx = redisAsyncConnect("localhost", 6379); - - if (m_ctx->err) { - cerr << "Error: " << m_ctx->errstr << endl; - redisAsyncFree(m_ctx); - emit finished(); - } - - m_adapter.setContext(m_ctx); - - redisAsyncCommand(m_ctx, NULL, NULL, "SET key %s", m_value); - redisAsyncCommand(m_ctx, getCallback, this, "GET key"); -} - -int main (int argc, char **argv) { - - QCoreApplication app(argc, argv); - - ExampleQt example(argv[argc-1]); - - QObject::connect(&example, SIGNAL(finished()), &app, SLOT(quit())); - QTimer::singleShot(0, &example, SLOT(run())); - - return app.exec(); -} diff --git a/ext/hiredis-0.14.1/examples/example-qt.h b/ext/hiredis-0.14.1/examples/example-qt.h deleted file mode 100644 index 374f47666..000000000 --- a/ext/hiredis-0.14.1/examples/example-qt.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __HIREDIS_EXAMPLE_QT_H -#define __HIREDIS_EXAMPLE_QT_H - -#include - -class ExampleQt : public QObject { - - Q_OBJECT - - public: - ExampleQt(const char * value, QObject * parent = 0) - : QObject(parent), m_value(value) {} - - signals: - void finished(); - - public slots: - void run(); - - private: - void finish() { emit finished(); } - - private: - const char * m_value; - redisAsyncContext * m_ctx; - RedisQtAdapter m_adapter; - - friend - void getCallback(redisAsyncContext *, void *, void *); -}; - -#endif /* !__HIREDIS_EXAMPLE_QT_H */ diff --git a/ext/hiredis-0.14.1/examples/example.c b/ext/hiredis-0.14.1/examples/example.c deleted file mode 100644 index 4d494c55a..000000000 --- a/ext/hiredis-0.14.1/examples/example.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include -#include - -#include - -int main(int argc, char **argv) { - unsigned int j; - redisContext *c; - redisReply *reply; - const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1"; - int port = (argc > 2) ? atoi(argv[2]) : 6379; - - struct timeval timeout = { 1, 500000 }; // 1.5 seconds - c = redisConnectWithTimeout(hostname, port, timeout); - if (c == NULL || c->err) { - if (c) { - printf("Connection error: %s\n", c->errstr); - redisFree(c); - } else { - printf("Connection error: can't allocate redis context\n"); - } - exit(1); - } - - /* PING server */ - reply = redisCommand(c,"PING"); - printf("PING: %s\n", reply->str); - freeReplyObject(reply); - - /* Set a key */ - reply = redisCommand(c,"SET %s %s", "foo", "hello world"); - printf("SET: %s\n", reply->str); - freeReplyObject(reply); - - /* Set a key using binary safe API */ - reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5); - printf("SET (binary API): %s\n", reply->str); - freeReplyObject(reply); - - /* Try a GET and two INCR */ - reply = redisCommand(c,"GET foo"); - printf("GET foo: %s\n", reply->str); - freeReplyObject(reply); - - reply = redisCommand(c,"INCR counter"); - printf("INCR counter: %lld\n", reply->integer); - freeReplyObject(reply); - /* again ... */ - reply = redisCommand(c,"INCR counter"); - printf("INCR counter: %lld\n", reply->integer); - freeReplyObject(reply); - - /* Create a list of numbers, from 0 to 9 */ - reply = redisCommand(c,"DEL mylist"); - freeReplyObject(reply); - for (j = 0; j < 10; j++) { - char buf[64]; - - snprintf(buf,64,"%u",j); - reply = redisCommand(c,"LPUSH mylist element-%s", buf); - freeReplyObject(reply); - } - - /* Let's check what we have inside the list */ - reply = redisCommand(c,"LRANGE mylist 0 -1"); - if (reply->type == REDIS_REPLY_ARRAY) { - for (j = 0; j < reply->elements; j++) { - printf("%u) %s\n", j, reply->element[j]->str); - } - } - freeReplyObject(reply); - - /* Disconnects and frees the context */ - redisFree(c); - - return 0; -} diff --git a/ext/hiredis-0.14.1/fmacros.h b/ext/hiredis-0.14.1/fmacros.h deleted file mode 100644 index 3227faafd..000000000 --- a/ext/hiredis-0.14.1/fmacros.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __HIREDIS_FMACRO_H -#define __HIREDIS_FMACRO_H - -#define _XOPEN_SOURCE 600 -#define _POSIX_C_SOURCE 200112L - -#if defined(__APPLE__) && defined(__MACH__) -/* Enable TCP_KEEPALIVE */ -#define _DARWIN_C_SOURCE -#endif - -#endif diff --git a/ext/hiredis-0.14.1/hiredis.c b/ext/hiredis-0.14.1/hiredis.c deleted file mode 100644 index 98f43c993..000000000 --- a/ext/hiredis-0.14.1/hiredis.c +++ /dev/null @@ -1,1006 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2014, Pieter Noordhuis - * Copyright (c) 2015, Matt Stancliff , - * Jan-Erik Rediger - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "fmacros.h" -#include -#include -#include -#include -#include -#include - -#include "hiredis.h" -#include "net.h" -#include "sds.h" - -static redisReply *createReplyObject(int type); -static void *createStringObject(const redisReadTask *task, char *str, size_t len); -static void *createArrayObject(const redisReadTask *task, int elements); -static void *createIntegerObject(const redisReadTask *task, long long value); -static void *createNilObject(const redisReadTask *task); - -/* Default set of functions to build the reply. Keep in mind that such a - * function returning NULL is interpreted as OOM. */ -static redisReplyObjectFunctions defaultFunctions = { - createStringObject, - createArrayObject, - createIntegerObject, - createNilObject, - freeReplyObject -}; - -/* Create a reply object */ -static redisReply *createReplyObject(int type) { - redisReply *r = calloc(1,sizeof(*r)); - - if (r == NULL) - return NULL; - - r->type = type; - return r; -} - -/* Free a reply object */ -void freeReplyObject(void *reply) { - redisReply *r = reply; - size_t j; - - if (r == NULL) - return; - - switch(r->type) { - case REDIS_REPLY_INTEGER: - break; /* Nothing to free */ - case REDIS_REPLY_ARRAY: - if (r->element != NULL) { - for (j = 0; j < r->elements; j++) - freeReplyObject(r->element[j]); - free(r->element); - } - break; - case REDIS_REPLY_ERROR: - case REDIS_REPLY_STATUS: - case REDIS_REPLY_STRING: - free(r->str); - break; - } - free(r); -} - -static void *createStringObject(const redisReadTask *task, char *str, size_t len) { - redisReply *r, *parent; - char *buf; - - r = createReplyObject(task->type); - if (r == NULL) - return NULL; - - buf = malloc(len+1); - if (buf == NULL) { - freeReplyObject(r); - return NULL; - } - - assert(task->type == REDIS_REPLY_ERROR || - task->type == REDIS_REPLY_STATUS || - task->type == REDIS_REPLY_STRING); - - /* Copy string value */ - memcpy(buf,str,len); - buf[len] = '\0'; - r->str = buf; - r->len = len; - - if (task->parent) { - parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); - parent->element[task->idx] = r; - } - return r; -} - -static void *createArrayObject(const redisReadTask *task, int elements) { - redisReply *r, *parent; - - r = createReplyObject(REDIS_REPLY_ARRAY); - if (r == NULL) - return NULL; - - if (elements > 0) { - r->element = calloc(elements,sizeof(redisReply*)); - if (r->element == NULL) { - freeReplyObject(r); - return NULL; - } - } - - r->elements = elements; - - if (task->parent) { - parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); - parent->element[task->idx] = r; - } - return r; -} - -static void *createIntegerObject(const redisReadTask *task, long long value) { - redisReply *r, *parent; - - r = createReplyObject(REDIS_REPLY_INTEGER); - if (r == NULL) - return NULL; - - r->integer = value; - - if (task->parent) { - parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); - parent->element[task->idx] = r; - } - return r; -} - -static void *createNilObject(const redisReadTask *task) { - redisReply *r, *parent; - - r = createReplyObject(REDIS_REPLY_NIL); - if (r == NULL) - return NULL; - - if (task->parent) { - parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); - parent->element[task->idx] = r; - } - return r; -} - -/* Return the number of digits of 'v' when converted to string in radix 10. - * Implementation borrowed from link in redis/src/util.c:string2ll(). */ -static uint32_t countDigits(uint64_t v) { - uint32_t result = 1; - for (;;) { - if (v < 10) return result; - if (v < 100) return result + 1; - if (v < 1000) return result + 2; - if (v < 10000) return result + 3; - v /= 10000U; - result += 4; - } -} - -/* Helper that calculates the bulk length given a certain string length. */ -static size_t bulklen(size_t len) { - return 1+countDigits(len)+2+len+2; -} - -int redisvFormatCommand(char **target, const char *format, va_list ap) { - const char *c = format; - char *cmd = NULL; /* final command */ - int pos; /* position in final command */ - sds curarg, newarg; /* current argument */ - int touched = 0; /* was the current argument touched? */ - char **curargv = NULL, **newargv = NULL; - int argc = 0; - int totlen = 0; - int error_type = 0; /* 0 = no error; -1 = memory error; -2 = format error */ - int j; - - /* Abort if there is not target to set */ - if (target == NULL) - return -1; - - /* Build the command string accordingly to protocol */ - curarg = sdsempty(); - if (curarg == NULL) - return -1; - - while(*c != '\0') { - if (*c != '%' || c[1] == '\0') { - if (*c == ' ') { - if (touched) { - newargv = realloc(curargv,sizeof(char*)*(argc+1)); - if (newargv == NULL) goto memory_err; - curargv = newargv; - curargv[argc++] = curarg; - totlen += bulklen(sdslen(curarg)); - - /* curarg is put in argv so it can be overwritten. */ - curarg = sdsempty(); - if (curarg == NULL) goto memory_err; - touched = 0; - } - } else { - newarg = sdscatlen(curarg,c,1); - if (newarg == NULL) goto memory_err; - curarg = newarg; - touched = 1; - } - } else { - char *arg; - size_t size; - - /* Set newarg so it can be checked even if it is not touched. */ - newarg = curarg; - - switch(c[1]) { - case 's': - arg = va_arg(ap,char*); - size = strlen(arg); - if (size > 0) - newarg = sdscatlen(curarg,arg,size); - break; - case 'b': - arg = va_arg(ap,char*); - size = va_arg(ap,size_t); - if (size > 0) - newarg = sdscatlen(curarg,arg,size); - break; - case '%': - newarg = sdscat(curarg,"%"); - break; - default: - /* Try to detect printf format */ - { - static const char intfmts[] = "diouxX"; - static const char flags[] = "#0-+ "; - char _format[16]; - const char *_p = c+1; - size_t _l = 0; - va_list _cpy; - - /* Flags */ - while (*_p != '\0' && strchr(flags,*_p) != NULL) _p++; - - /* Field width */ - while (*_p != '\0' && isdigit(*_p)) _p++; - - /* Precision */ - if (*_p == '.') { - _p++; - while (*_p != '\0' && isdigit(*_p)) _p++; - } - - /* Copy va_list before consuming with va_arg */ - va_copy(_cpy,ap); - - /* Integer conversion (without modifiers) */ - if (strchr(intfmts,*_p) != NULL) { - va_arg(ap,int); - goto fmt_valid; - } - - /* Double conversion (without modifiers) */ - if (strchr("eEfFgGaA",*_p) != NULL) { - va_arg(ap,double); - goto fmt_valid; - } - - /* Size: char */ - if (_p[0] == 'h' && _p[1] == 'h') { - _p += 2; - if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { - va_arg(ap,int); /* char gets promoted to int */ - goto fmt_valid; - } - goto fmt_invalid; - } - - /* Size: short */ - if (_p[0] == 'h') { - _p += 1; - if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { - va_arg(ap,int); /* short gets promoted to int */ - goto fmt_valid; - } - goto fmt_invalid; - } - - /* Size: long long */ - if (_p[0] == 'l' && _p[1] == 'l') { - _p += 2; - if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { - va_arg(ap,long long); - goto fmt_valid; - } - goto fmt_invalid; - } - - /* Size: long */ - if (_p[0] == 'l') { - _p += 1; - if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { - va_arg(ap,long); - goto fmt_valid; - } - goto fmt_invalid; - } - - fmt_invalid: - va_end(_cpy); - goto format_err; - - fmt_valid: - _l = (_p+1)-c; - if (_l < sizeof(_format)-2) { - memcpy(_format,c,_l); - _format[_l] = '\0'; - newarg = sdscatvprintf(curarg,_format,_cpy); - - /* Update current position (note: outer blocks - * increment c twice so compensate here) */ - c = _p-1; - } - - va_end(_cpy); - break; - } - } - - if (newarg == NULL) goto memory_err; - curarg = newarg; - - touched = 1; - c++; - } - c++; - } - - /* Add the last argument if needed */ - if (touched) { - newargv = realloc(curargv,sizeof(char*)*(argc+1)); - if (newargv == NULL) goto memory_err; - curargv = newargv; - curargv[argc++] = curarg; - totlen += bulklen(sdslen(curarg)); - } else { - sdsfree(curarg); - } - - /* Clear curarg because it was put in curargv or was free'd. */ - curarg = NULL; - - /* Add bytes needed to hold multi bulk count */ - totlen += 1+countDigits(argc)+2; - - /* Build the command at protocol level */ - cmd = malloc(totlen+1); - if (cmd == NULL) goto memory_err; - - pos = sprintf(cmd,"*%d\r\n",argc); - for (j = 0; j < argc; j++) { - pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j])); - memcpy(cmd+pos,curargv[j],sdslen(curargv[j])); - pos += sdslen(curargv[j]); - sdsfree(curargv[j]); - cmd[pos++] = '\r'; - cmd[pos++] = '\n'; - } - assert(pos == totlen); - cmd[pos] = '\0'; - - free(curargv); - *target = cmd; - return totlen; - -format_err: - error_type = -2; - goto cleanup; - -memory_err: - error_type = -1; - goto cleanup; - -cleanup: - if (curargv) { - while(argc--) - sdsfree(curargv[argc]); - free(curargv); - } - - sdsfree(curarg); - free(cmd); - - return error_type; -} - -/* Format a command according to the Redis protocol. This function - * takes a format similar to printf: - * - * %s represents a C null terminated string you want to interpolate - * %b represents a binary safe string - * - * When using %b you need to provide both the pointer to the string - * and the length in bytes as a size_t. Examples: - * - * len = redisFormatCommand(target, "GET %s", mykey); - * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen); - */ -int redisFormatCommand(char **target, const char *format, ...) { - va_list ap; - int len; - va_start(ap,format); - len = redisvFormatCommand(target,format,ap); - va_end(ap); - - /* The API says "-1" means bad result, but we now also return "-2" in some - * cases. Force the return value to always be -1. */ - if (len < 0) - len = -1; - - return len; -} - -/* Format a command according to the Redis protocol using an sds string and - * sdscatfmt for the processing of arguments. This function takes the - * number of arguments, an array with arguments and an array with their - * lengths. If the latter is set to NULL, strlen will be used to compute the - * argument lengths. - */ -int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv, - const size_t *argvlen) -{ - sds cmd; - unsigned long long totlen; - int j; - size_t len; - - /* Abort on a NULL target */ - if (target == NULL) - return -1; - - /* Calculate our total size */ - totlen = 1+countDigits(argc)+2; - for (j = 0; j < argc; j++) { - len = argvlen ? argvlen[j] : strlen(argv[j]); - totlen += bulklen(len); - } - - /* Use an SDS string for command construction */ - cmd = sdsempty(); - if (cmd == NULL) - return -1; - - /* We already know how much storage we need */ - cmd = sdsMakeRoomFor(cmd, totlen); - if (cmd == NULL) - return -1; - - /* Construct command */ - cmd = sdscatfmt(cmd, "*%i\r\n", argc); - for (j=0; j < argc; j++) { - len = argvlen ? argvlen[j] : strlen(argv[j]); - cmd = sdscatfmt(cmd, "$%u\r\n", len); - cmd = sdscatlen(cmd, argv[j], len); - cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1); - } - - assert(sdslen(cmd)==totlen); - - *target = cmd; - return totlen; -} - -void redisFreeSdsCommand(sds cmd) { - sdsfree(cmd); -} - -/* Format a command according to the Redis protocol. This function takes the - * number of arguments, an array with arguments and an array with their - * lengths. If the latter is set to NULL, strlen will be used to compute the - * argument lengths. - */ -int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) { - char *cmd = NULL; /* final command */ - int pos; /* position in final command */ - size_t len; - int totlen, j; - - /* Abort on a NULL target */ - if (target == NULL) - return -1; - - /* Calculate number of bytes needed for the command */ - totlen = 1+countDigits(argc)+2; - for (j = 0; j < argc; j++) { - len = argvlen ? argvlen[j] : strlen(argv[j]); - totlen += bulklen(len); - } - - /* Build the command at protocol level */ - cmd = malloc(totlen+1); - if (cmd == NULL) - return -1; - - pos = sprintf(cmd,"*%d\r\n",argc); - for (j = 0; j < argc; j++) { - len = argvlen ? argvlen[j] : strlen(argv[j]); - pos += sprintf(cmd+pos,"$%zu\r\n",len); - memcpy(cmd+pos,argv[j],len); - pos += len; - cmd[pos++] = '\r'; - cmd[pos++] = '\n'; - } - assert(pos == totlen); - cmd[pos] = '\0'; - - *target = cmd; - return totlen; -} - -void redisFreeCommand(char *cmd) { - free(cmd); -} - -void __redisSetError(redisContext *c, int type, const char *str) { - size_t len; - - c->err = type; - if (str != NULL) { - len = strlen(str); - len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1); - memcpy(c->errstr,str,len); - c->errstr[len] = '\0'; - } else { - /* Only REDIS_ERR_IO may lack a description! */ - assert(type == REDIS_ERR_IO); - strerror_r(errno, c->errstr, sizeof(c->errstr)); - } -} - -redisReader *redisReaderCreate(void) { - return redisReaderCreateWithFunctions(&defaultFunctions); -} - -static redisContext *redisContextInit(void) { - redisContext *c; - - c = calloc(1,sizeof(redisContext)); - if (c == NULL) - return NULL; - - c->obuf = sdsempty(); - c->reader = redisReaderCreate(); - - if (c->obuf == NULL || c->reader == NULL) { - redisFree(c); - return NULL; - } - - return c; -} - -void redisFree(redisContext *c) { - if (c == NULL) - return; - if (c->fd > 0) - close(c->fd); - sdsfree(c->obuf); - redisReaderFree(c->reader); - free(c->tcp.host); - free(c->tcp.source_addr); - free(c->unix_sock.path); - free(c->timeout); - free(c); -} - -int redisFreeKeepFd(redisContext *c) { - int fd = c->fd; - c->fd = -1; - redisFree(c); - return fd; -} - -int redisReconnect(redisContext *c) { - c->err = 0; - memset(c->errstr, '\0', strlen(c->errstr)); - - if (c->fd > 0) { - close(c->fd); - } - - sdsfree(c->obuf); - redisReaderFree(c->reader); - - c->obuf = sdsempty(); - c->reader = redisReaderCreate(); - - if (c->connection_type == REDIS_CONN_TCP) { - return redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port, - c->timeout, c->tcp.source_addr); - } else if (c->connection_type == REDIS_CONN_UNIX) { - return redisContextConnectUnix(c, c->unix_sock.path, c->timeout); - } else { - /* Something bad happened here and shouldn't have. There isn't - enough information in the context to reconnect. */ - __redisSetError(c,REDIS_ERR_OTHER,"Not enough information to reconnect"); - } - - return REDIS_ERR; -} - -/* Connect to a Redis instance. On error the field error in the returned - * context will be set to the return value of the error function. - * When no set of reply functions is given, the default set will be used. */ -redisContext *redisConnect(const char *ip, int port) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags |= REDIS_BLOCK; - redisContextConnectTcp(c,ip,port,NULL); - return c; -} - -redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags |= REDIS_BLOCK; - redisContextConnectTcp(c,ip,port,&tv); - return c; -} - -redisContext *redisConnectNonBlock(const char *ip, int port) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags &= ~REDIS_BLOCK; - redisContextConnectTcp(c,ip,port,NULL); - return c; -} - -redisContext *redisConnectBindNonBlock(const char *ip, int port, - const char *source_addr) { - redisContext *c = redisContextInit(); - if (c == NULL) - return NULL; - c->flags &= ~REDIS_BLOCK; - redisContextConnectBindTcp(c,ip,port,NULL,source_addr); - return c; -} - -redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, - const char *source_addr) { - redisContext *c = redisContextInit(); - if (c == NULL) - return NULL; - c->flags &= ~REDIS_BLOCK; - c->flags |= REDIS_REUSEADDR; - redisContextConnectBindTcp(c,ip,port,NULL,source_addr); - return c; -} - -redisContext *redisConnectUnix(const char *path) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags |= REDIS_BLOCK; - redisContextConnectUnix(c,path,NULL); - return c; -} - -redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags |= REDIS_BLOCK; - redisContextConnectUnix(c,path,&tv); - return c; -} - -redisContext *redisConnectUnixNonBlock(const char *path) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags &= ~REDIS_BLOCK; - redisContextConnectUnix(c,path,NULL); - return c; -} - -redisContext *redisConnectFd(int fd) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->fd = fd; - c->flags |= REDIS_BLOCK | REDIS_CONNECTED; - return c; -} - -/* Set read/write timeout on a blocking socket. */ -int redisSetTimeout(redisContext *c, const struct timeval tv) { - if (c->flags & REDIS_BLOCK) - return redisContextSetTimeout(c,tv); - return REDIS_ERR; -} - -/* Enable connection KeepAlive. */ -int redisEnableKeepAlive(redisContext *c) { - if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK) - return REDIS_ERR; - return REDIS_OK; -} - -/* Use this function to handle a read event on the descriptor. It will try - * and read some bytes from the socket and feed them to the reply parser. - * - * After this function is called, you may use redisContextReadReply to - * see if there is a reply available. */ -int redisBufferRead(redisContext *c) { - char buf[1024*16]; - int nread; - - /* Return early when the context has seen an error. */ - if (c->err) - return REDIS_ERR; - - nread = read(c->fd,buf,sizeof(buf)); - if (nread == -1) { - if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { - /* Try again later */ - } else { - __redisSetError(c,REDIS_ERR_IO,NULL); - return REDIS_ERR; - } - } else if (nread == 0) { - __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection"); - return REDIS_ERR; - } else { - if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) { - __redisSetError(c,c->reader->err,c->reader->errstr); - return REDIS_ERR; - } - } - return REDIS_OK; -} - -/* Write the output buffer to the socket. - * - * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was - * successfully written to the socket. When the buffer is empty after the - * write operation, "done" is set to 1 (if given). - * - * Returns REDIS_ERR if an error occurred trying to write and sets - * c->errstr to hold the appropriate error string. - */ -int redisBufferWrite(redisContext *c, int *done) { - int nwritten; - - /* Return early when the context has seen an error. */ - if (c->err) - return REDIS_ERR; - - if (sdslen(c->obuf) > 0) { - nwritten = write(c->fd,c->obuf,sdslen(c->obuf)); - if (nwritten == -1) { - if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { - /* Try again later */ - } else { - __redisSetError(c,REDIS_ERR_IO,NULL); - return REDIS_ERR; - } - } else if (nwritten > 0) { - if (nwritten == (signed)sdslen(c->obuf)) { - sdsfree(c->obuf); - c->obuf = sdsempty(); - } else { - sdsrange(c->obuf,nwritten,-1); - } - } - } - if (done != NULL) *done = (sdslen(c->obuf) == 0); - return REDIS_OK; -} - -/* Internal helper function to try and get a reply from the reader, - * or set an error in the context otherwise. */ -int redisGetReplyFromReader(redisContext *c, void **reply) { - if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) { - __redisSetError(c,c->reader->err,c->reader->errstr); - return REDIS_ERR; - } - return REDIS_OK; -} - -int redisGetReply(redisContext *c, void **reply) { - int wdone = 0; - void *aux = NULL; - - /* Try to read pending replies */ - if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) - return REDIS_ERR; - - /* For the blocking context, flush output buffer and read reply */ - if (aux == NULL && c->flags & REDIS_BLOCK) { - /* Write until done */ - do { - if (redisBufferWrite(c,&wdone) == REDIS_ERR) - return REDIS_ERR; - } while (!wdone); - - /* Read until there is a reply */ - do { - if (redisBufferRead(c) == REDIS_ERR) - return REDIS_ERR; - if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) - return REDIS_ERR; - } while (aux == NULL); - } - - /* Set reply object */ - if (reply != NULL) *reply = aux; - return REDIS_OK; -} - - -/* Helper function for the redisAppendCommand* family of functions. - * - * Write a formatted command to the output buffer. When this family - * is used, you need to call redisGetReply yourself to retrieve - * the reply (or replies in pub/sub). - */ -int __redisAppendCommand(redisContext *c, const char *cmd, size_t len) { - sds newbuf; - - newbuf = sdscatlen(c->obuf,cmd,len); - if (newbuf == NULL) { - __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); - return REDIS_ERR; - } - - c->obuf = newbuf; - return REDIS_OK; -} - -int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len) { - - if (__redisAppendCommand(c, cmd, len) != REDIS_OK) { - return REDIS_ERR; - } - - return REDIS_OK; -} - -int redisvAppendCommand(redisContext *c, const char *format, va_list ap) { - char *cmd; - int len; - - len = redisvFormatCommand(&cmd,format,ap); - if (len == -1) { - __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); - return REDIS_ERR; - } else if (len == -2) { - __redisSetError(c,REDIS_ERR_OTHER,"Invalid format string"); - return REDIS_ERR; - } - - if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { - free(cmd); - return REDIS_ERR; - } - - free(cmd); - return REDIS_OK; -} - -int redisAppendCommand(redisContext *c, const char *format, ...) { - va_list ap; - int ret; - - va_start(ap,format); - ret = redisvAppendCommand(c,format,ap); - va_end(ap); - return ret; -} - -int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { - sds cmd; - int len; - - len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen); - if (len == -1) { - __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); - return REDIS_ERR; - } - - if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { - sdsfree(cmd); - return REDIS_ERR; - } - - sdsfree(cmd); - return REDIS_OK; -} - -/* Helper function for the redisCommand* family of functions. - * - * Write a formatted command to the output buffer. If the given context is - * blocking, immediately read the reply into the "reply" pointer. When the - * context is non-blocking, the "reply" pointer will not be used and the - * command is simply appended to the write buffer. - * - * Returns the reply when a reply was successfully retrieved. Returns NULL - * otherwise. When NULL is returned in a blocking context, the error field - * in the context will be set. - */ -static void *__redisBlockForReply(redisContext *c) { - void *reply; - - if (c->flags & REDIS_BLOCK) { - if (redisGetReply(c,&reply) != REDIS_OK) - return NULL; - return reply; - } - return NULL; -} - -void *redisvCommand(redisContext *c, const char *format, va_list ap) { - if (redisvAppendCommand(c,format,ap) != REDIS_OK) - return NULL; - return __redisBlockForReply(c); -} - -void *redisCommand(redisContext *c, const char *format, ...) { - va_list ap; - va_start(ap,format); - void *reply = redisvCommand(c,format,ap); - va_end(ap); - return reply; -} - -void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { - if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK) - return NULL; - return __redisBlockForReply(c); -} diff --git a/ext/hiredis-0.14.1/hiredis.h b/ext/hiredis-0.14.1/hiredis.h deleted file mode 100644 index d945bf204..000000000 --- a/ext/hiredis-0.14.1/hiredis.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2014, Pieter Noordhuis - * Copyright (c) 2015, Matt Stancliff , - * Jan-Erik Rediger - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_H -#define __HIREDIS_H -#include "read.h" -#include /* for va_list */ -#include /* for struct timeval */ -#include /* uintXX_t, etc */ -#include "sds.h" /* for sds */ -#include "alloc.h" /* for allocation wrappers */ - -#define HIREDIS_MAJOR 0 -#define HIREDIS_MINOR 14 -#define HIREDIS_PATCH 1 -#define HIREDIS_SONAME 0.14 - -/* Connection type can be blocking or non-blocking and is set in the - * least significant bit of the flags field in redisContext. */ -#define REDIS_BLOCK 0x1 - -/* Connection may be disconnected before being free'd. The second bit - * in the flags field is set when the context is connected. */ -#define REDIS_CONNECTED 0x2 - -/* The async API might try to disconnect cleanly and flush the output - * buffer and read all subsequent replies before disconnecting. - * This flag means no new commands can come in and the connection - * should be terminated once all replies have been read. */ -#define REDIS_DISCONNECTING 0x4 - -/* Flag specific to the async API which means that the context should be clean - * up as soon as possible. */ -#define REDIS_FREEING 0x8 - -/* Flag that is set when an async callback is executed. */ -#define REDIS_IN_CALLBACK 0x10 - -/* Flag that is set when the async context has one or more subscriptions. */ -#define REDIS_SUBSCRIBED 0x20 - -/* Flag that is set when monitor mode is active */ -#define REDIS_MONITORING 0x40 - -/* Flag that is set when we should set SO_REUSEADDR before calling bind() */ -#define REDIS_REUSEADDR 0x80 - -#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */ - -/* number of times we retry to connect in the case of EADDRNOTAVAIL and - * SO_REUSEADDR is being used. */ -#define REDIS_CONNECT_RETRIES 10 - -#ifdef __cplusplus -extern "C" { -#endif - -/* This is the reply object returned by redisCommand() */ -typedef struct redisReply { - int type; /* REDIS_REPLY_* */ - long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ - size_t len; /* Length of string */ - char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */ - size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ - struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ -} redisReply; - -redisReader *redisReaderCreate(void); - -/* Function to free the reply objects hiredis returns by default. */ -void freeReplyObject(void *reply); - -/* Functions to format a command according to the protocol. */ -int redisvFormatCommand(char **target, const char *format, va_list ap); -int redisFormatCommand(char **target, const char *format, ...); -int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen); -int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen); -void redisFreeCommand(char *cmd); -void redisFreeSdsCommand(sds cmd); - -enum redisConnectionType { - REDIS_CONN_TCP, - REDIS_CONN_UNIX -}; - -/* Context for a connection to Redis */ -typedef struct redisContext { - int err; /* Error flags, 0 when there is no error */ - char errstr[128]; /* String representation of error when applicable */ - int fd; - int flags; - char *obuf; /* Write buffer */ - redisReader *reader; /* Protocol reader */ - - enum redisConnectionType connection_type; - struct timeval *timeout; - - struct { - char *host; - char *source_addr; - int port; - } tcp; - - struct { - char *path; - } unix_sock; - -} redisContext; - -redisContext *redisConnect(const char *ip, int port); -redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); -redisContext *redisConnectNonBlock(const char *ip, int port); -redisContext *redisConnectBindNonBlock(const char *ip, int port, - const char *source_addr); -redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, - const char *source_addr); -redisContext *redisConnectUnix(const char *path); -redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); -redisContext *redisConnectUnixNonBlock(const char *path); -redisContext *redisConnectFd(int fd); - -/** - * Reconnect the given context using the saved information. - * - * This re-uses the exact same connect options as in the initial connection. - * host, ip (or path), timeout and bind address are reused, - * flags are used unmodified from the existing context. - * - * Returns REDIS_OK on successful connect or REDIS_ERR otherwise. - */ -int redisReconnect(redisContext *c); - -int redisSetTimeout(redisContext *c, const struct timeval tv); -int redisEnableKeepAlive(redisContext *c); -void redisFree(redisContext *c); -int redisFreeKeepFd(redisContext *c); -int redisBufferRead(redisContext *c); -int redisBufferWrite(redisContext *c, int *done); - -/* In a blocking context, this function first checks if there are unconsumed - * replies to return and returns one if so. Otherwise, it flushes the output - * buffer to the socket and reads until it has a reply. In a non-blocking - * context, it will return unconsumed replies until there are no more. */ -int redisGetReply(redisContext *c, void **reply); -int redisGetReplyFromReader(redisContext *c, void **reply); - -/* Write a formatted command to the output buffer. Use these functions in blocking mode - * to get a pipeline of commands. */ -int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len); - -/* Write a command to the output buffer. Use these functions in blocking mode - * to get a pipeline of commands. */ -int redisvAppendCommand(redisContext *c, const char *format, va_list ap); -int redisAppendCommand(redisContext *c, const char *format, ...); -int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); - -/* Issue a command to Redis. In a blocking context, it is identical to calling - * redisAppendCommand, followed by redisGetReply. The function will return - * NULL if there was an error in performing the request, otherwise it will - * return the reply. In a non-blocking context, it is identical to calling - * only redisAppendCommand and will always return NULL. */ -void *redisvCommand(redisContext *c, const char *format, va_list ap); -void *redisCommand(redisContext *c, const char *format, ...); -void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/ext/hiredis-0.14.1/include/hiredis/alloc.h b/ext/hiredis-0.14.1/include/hiredis/alloc.h deleted file mode 100644 index 2c9b04e35..000000000 --- a/ext/hiredis-0.14.1/include/hiredis/alloc.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2020, Michael Grunder - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef HIREDIS_ALLOC_H -#define HIREDIS_ALLOC_H - -#include /* for size_t */ - -#ifndef HIREDIS_OOM_HANDLER -#define HIREDIS_OOM_HANDLER abort() -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -void *hi_malloc(size_t size); -void *hi_calloc(size_t nmemb, size_t size); -void *hi_realloc(void *ptr, size_t size); -char *hi_strdup(const char *str); - -#ifdef __cplusplus -} -#endif - -#endif /* HIREDIS_ALLOC_H */ diff --git a/ext/hiredis-0.14.1/include/hiredis/async.h b/ext/hiredis-0.14.1/include/hiredis/async.h deleted file mode 100644 index e69d84090..000000000 --- a/ext/hiredis-0.14.1/include/hiredis/async.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_ASYNC_H -#define __HIREDIS_ASYNC_H -#include "hiredis.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct redisAsyncContext; /* need forward declaration of redisAsyncContext */ -struct dict; /* dictionary header is included in async.c */ - -/* Reply callback prototype and container */ -typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*); -typedef struct redisCallback { - struct redisCallback *next; /* simple singly linked list */ - redisCallbackFn *fn; - int pending_subs; - void *privdata; -} redisCallback; - -/* List of callbacks for either regular replies or pub/sub */ -typedef struct redisCallbackList { - redisCallback *head, *tail; -} redisCallbackList; - -/* Connection callback prototypes */ -typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status); -typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status); - -/* Context for an async connection to Redis */ -typedef struct redisAsyncContext { - /* Hold the regular context, so it can be realloc'ed. */ - redisContext c; - - /* Setup error flags so they can be used directly. */ - int err; - char *errstr; - - /* Not used by hiredis */ - void *data; - - /* Event library data and hooks */ - struct { - void *data; - - /* Hooks that are called when the library expects to start - * reading/writing. These functions should be idempotent. */ - void (*addRead)(void *privdata); - void (*delRead)(void *privdata); - void (*addWrite)(void *privdata); - void (*delWrite)(void *privdata); - void (*cleanup)(void *privdata); - } ev; - - /* Called when either the connection is terminated due to an error or per - * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */ - redisDisconnectCallback *onDisconnect; - - /* Called when the first write event was received. */ - redisConnectCallback *onConnect; - - /* Regular command callbacks */ - redisCallbackList replies; - - /* Subscription callbacks */ - struct { - redisCallbackList invalid; - struct dict *channels; - struct dict *patterns; - } sub; -} redisAsyncContext; - -/* Functions that proxy to hiredis */ -redisAsyncContext *redisAsyncConnect(const char *ip, int port); -redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr); -redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, - const char *source_addr); -redisAsyncContext *redisAsyncConnectUnix(const char *path); -int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); -int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); -void redisAsyncDisconnect(redisAsyncContext *ac); -void redisAsyncFree(redisAsyncContext *ac); - -/* Handle read/write events */ -void redisAsyncHandleRead(redisAsyncContext *ac); -void redisAsyncHandleWrite(redisAsyncContext *ac); - -/* Command functions for an async context. Write the command to the - * output buffer and register the provided callback. */ -int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap); -int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...); -int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen); -int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/ext/hiredis-0.14.1/include/hiredis/dict.h b/ext/hiredis-0.14.1/include/hiredis/dict.h deleted file mode 100644 index 95fcd280e..000000000 --- a/ext/hiredis-0.14.1/include/hiredis/dict.h +++ /dev/null @@ -1,126 +0,0 @@ -/* Hash table implementation. - * - * This file implements in memory hash tables with insert/del/replace/find/ - * get-random-element operations. Hash tables will auto resize if needed - * tables of power of two in size are used, collisions are handled by - * chaining. See the source code for more information... :) - * - * Copyright (c) 2006-2010, Salvatore Sanfilippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __DICT_H -#define __DICT_H - -#define DICT_OK 0 -#define DICT_ERR 1 - -/* Unused arguments generate annoying warnings... */ -#define DICT_NOTUSED(V) ((void) V) - -typedef struct dictEntry { - void *key; - void *val; - struct dictEntry *next; -} dictEntry; - -typedef struct dictType { - unsigned int (*hashFunction)(const void *key); - void *(*keyDup)(void *privdata, const void *key); - void *(*valDup)(void *privdata, const void *obj); - int (*keyCompare)(void *privdata, const void *key1, const void *key2); - void (*keyDestructor)(void *privdata, void *key); - void (*valDestructor)(void *privdata, void *obj); -} dictType; - -typedef struct dict { - dictEntry **table; - dictType *type; - unsigned long size; - unsigned long sizemask; - unsigned long used; - void *privdata; -} dict; - -typedef struct dictIterator { - dict *ht; - int index; - dictEntry *entry, *nextEntry; -} dictIterator; - -/* This is the initial size of every hash table */ -#define DICT_HT_INITIAL_SIZE 4 - -/* ------------------------------- Macros ------------------------------------*/ -#define dictFreeEntryVal(ht, entry) \ - if ((ht)->type->valDestructor) \ - (ht)->type->valDestructor((ht)->privdata, (entry)->val) - -#define dictSetHashVal(ht, entry, _val_) do { \ - if ((ht)->type->valDup) \ - entry->val = (ht)->type->valDup((ht)->privdata, _val_); \ - else \ - entry->val = (_val_); \ -} while(0) - -#define dictFreeEntryKey(ht, entry) \ - if ((ht)->type->keyDestructor) \ - (ht)->type->keyDestructor((ht)->privdata, (entry)->key) - -#define dictSetHashKey(ht, entry, _key_) do { \ - if ((ht)->type->keyDup) \ - entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \ - else \ - entry->key = (_key_); \ -} while(0) - -#define dictCompareHashKeys(ht, key1, key2) \ - (((ht)->type->keyCompare) ? \ - (ht)->type->keyCompare((ht)->privdata, key1, key2) : \ - (key1) == (key2)) - -#define dictHashKey(ht, key) (ht)->type->hashFunction(key) - -#define dictGetEntryKey(he) ((he)->key) -#define dictGetEntryVal(he) ((he)->val) -#define dictSlots(ht) ((ht)->size) -#define dictSize(ht) ((ht)->used) - -/* API */ -static unsigned int dictGenHashFunction(const unsigned char *buf, int len); -static dict *dictCreate(dictType *type, void *privDataPtr); -static int dictExpand(dict *ht, unsigned long size); -static int dictAdd(dict *ht, void *key, void *val); -static int dictReplace(dict *ht, void *key, void *val); -static int dictDelete(dict *ht, const void *key); -static void dictRelease(dict *ht); -static dictEntry * dictFind(dict *ht, const void *key); -static dictIterator *dictGetIterator(dict *ht); -static dictEntry *dictNext(dictIterator *iter); -static void dictReleaseIterator(dictIterator *iter); - -#endif /* __DICT_H */ diff --git a/ext/hiredis-0.14.1/include/hiredis/fmacros.h b/ext/hiredis-0.14.1/include/hiredis/fmacros.h deleted file mode 100644 index 3227faafd..000000000 --- a/ext/hiredis-0.14.1/include/hiredis/fmacros.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __HIREDIS_FMACRO_H -#define __HIREDIS_FMACRO_H - -#define _XOPEN_SOURCE 600 -#define _POSIX_C_SOURCE 200112L - -#if defined(__APPLE__) && defined(__MACH__) -/* Enable TCP_KEEPALIVE */ -#define _DARWIN_C_SOURCE -#endif - -#endif diff --git a/ext/hiredis-0.14.1/include/hiredis/hiredis.h b/ext/hiredis-0.14.1/include/hiredis/hiredis.h deleted file mode 100644 index d945bf204..000000000 --- a/ext/hiredis-0.14.1/include/hiredis/hiredis.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2014, Pieter Noordhuis - * Copyright (c) 2015, Matt Stancliff , - * Jan-Erik Rediger - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_H -#define __HIREDIS_H -#include "read.h" -#include /* for va_list */ -#include /* for struct timeval */ -#include /* uintXX_t, etc */ -#include "sds.h" /* for sds */ -#include "alloc.h" /* for allocation wrappers */ - -#define HIREDIS_MAJOR 0 -#define HIREDIS_MINOR 14 -#define HIREDIS_PATCH 1 -#define HIREDIS_SONAME 0.14 - -/* Connection type can be blocking or non-blocking and is set in the - * least significant bit of the flags field in redisContext. */ -#define REDIS_BLOCK 0x1 - -/* Connection may be disconnected before being free'd. The second bit - * in the flags field is set when the context is connected. */ -#define REDIS_CONNECTED 0x2 - -/* The async API might try to disconnect cleanly and flush the output - * buffer and read all subsequent replies before disconnecting. - * This flag means no new commands can come in and the connection - * should be terminated once all replies have been read. */ -#define REDIS_DISCONNECTING 0x4 - -/* Flag specific to the async API which means that the context should be clean - * up as soon as possible. */ -#define REDIS_FREEING 0x8 - -/* Flag that is set when an async callback is executed. */ -#define REDIS_IN_CALLBACK 0x10 - -/* Flag that is set when the async context has one or more subscriptions. */ -#define REDIS_SUBSCRIBED 0x20 - -/* Flag that is set when monitor mode is active */ -#define REDIS_MONITORING 0x40 - -/* Flag that is set when we should set SO_REUSEADDR before calling bind() */ -#define REDIS_REUSEADDR 0x80 - -#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */ - -/* number of times we retry to connect in the case of EADDRNOTAVAIL and - * SO_REUSEADDR is being used. */ -#define REDIS_CONNECT_RETRIES 10 - -#ifdef __cplusplus -extern "C" { -#endif - -/* This is the reply object returned by redisCommand() */ -typedef struct redisReply { - int type; /* REDIS_REPLY_* */ - long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ - size_t len; /* Length of string */ - char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */ - size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ - struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ -} redisReply; - -redisReader *redisReaderCreate(void); - -/* Function to free the reply objects hiredis returns by default. */ -void freeReplyObject(void *reply); - -/* Functions to format a command according to the protocol. */ -int redisvFormatCommand(char **target, const char *format, va_list ap); -int redisFormatCommand(char **target, const char *format, ...); -int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen); -int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen); -void redisFreeCommand(char *cmd); -void redisFreeSdsCommand(sds cmd); - -enum redisConnectionType { - REDIS_CONN_TCP, - REDIS_CONN_UNIX -}; - -/* Context for a connection to Redis */ -typedef struct redisContext { - int err; /* Error flags, 0 when there is no error */ - char errstr[128]; /* String representation of error when applicable */ - int fd; - int flags; - char *obuf; /* Write buffer */ - redisReader *reader; /* Protocol reader */ - - enum redisConnectionType connection_type; - struct timeval *timeout; - - struct { - char *host; - char *source_addr; - int port; - } tcp; - - struct { - char *path; - } unix_sock; - -} redisContext; - -redisContext *redisConnect(const char *ip, int port); -redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); -redisContext *redisConnectNonBlock(const char *ip, int port); -redisContext *redisConnectBindNonBlock(const char *ip, int port, - const char *source_addr); -redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, - const char *source_addr); -redisContext *redisConnectUnix(const char *path); -redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); -redisContext *redisConnectUnixNonBlock(const char *path); -redisContext *redisConnectFd(int fd); - -/** - * Reconnect the given context using the saved information. - * - * This re-uses the exact same connect options as in the initial connection. - * host, ip (or path), timeout and bind address are reused, - * flags are used unmodified from the existing context. - * - * Returns REDIS_OK on successful connect or REDIS_ERR otherwise. - */ -int redisReconnect(redisContext *c); - -int redisSetTimeout(redisContext *c, const struct timeval tv); -int redisEnableKeepAlive(redisContext *c); -void redisFree(redisContext *c); -int redisFreeKeepFd(redisContext *c); -int redisBufferRead(redisContext *c); -int redisBufferWrite(redisContext *c, int *done); - -/* In a blocking context, this function first checks if there are unconsumed - * replies to return and returns one if so. Otherwise, it flushes the output - * buffer to the socket and reads until it has a reply. In a non-blocking - * context, it will return unconsumed replies until there are no more. */ -int redisGetReply(redisContext *c, void **reply); -int redisGetReplyFromReader(redisContext *c, void **reply); - -/* Write a formatted command to the output buffer. Use these functions in blocking mode - * to get a pipeline of commands. */ -int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len); - -/* Write a command to the output buffer. Use these functions in blocking mode - * to get a pipeline of commands. */ -int redisvAppendCommand(redisContext *c, const char *format, va_list ap); -int redisAppendCommand(redisContext *c, const char *format, ...); -int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); - -/* Issue a command to Redis. In a blocking context, it is identical to calling - * redisAppendCommand, followed by redisGetReply. The function will return - * NULL if there was an error in performing the request, otherwise it will - * return the reply. In a non-blocking context, it is identical to calling - * only redisAppendCommand and will always return NULL. */ -void *redisvCommand(redisContext *c, const char *format, va_list ap); -void *redisCommand(redisContext *c, const char *format, ...); -void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/ext/hiredis-0.14.1/include/hiredis/net.h b/ext/hiredis-0.14.1/include/hiredis/net.h deleted file mode 100644 index d9dc36257..000000000 --- a/ext/hiredis-0.14.1/include/hiredis/net.h +++ /dev/null @@ -1,49 +0,0 @@ -/* Extracted from anet.c to work properly with Hiredis error reporting. - * - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2014, Pieter Noordhuis - * Copyright (c) 2015, Matt Stancliff , - * Jan-Erik Rediger - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __NET_H -#define __NET_H - -#include "hiredis.h" - -int redisCheckSocketError(redisContext *c); -int redisContextSetTimeout(redisContext *c, const struct timeval tv); -int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); -int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, - const struct timeval *timeout, - const char *source_addr); -int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout); -int redisKeepAlive(redisContext *c, int interval); - -#endif diff --git a/ext/hiredis-0.14.1/include/hiredis/read.h b/ext/hiredis-0.14.1/include/hiredis/read.h deleted file mode 100644 index 2988aa453..000000000 --- a/ext/hiredis-0.14.1/include/hiredis/read.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - - -#ifndef __HIREDIS_READ_H -#define __HIREDIS_READ_H -#include /* for size_t */ - -#define REDIS_ERR -1 -#define REDIS_OK 0 - -/* When an error occurs, the err flag in a context is set to hold the type of - * error that occurred. REDIS_ERR_IO means there was an I/O error and you - * should use the "errno" variable to find out what is wrong. - * For other values, the "errstr" field will hold a description. */ -#define REDIS_ERR_IO 1 /* Error in read or write */ -#define REDIS_ERR_EOF 3 /* End of file */ -#define REDIS_ERR_PROTOCOL 4 /* Protocol error */ -#define REDIS_ERR_OOM 5 /* Out of memory */ -#define REDIS_ERR_OTHER 2 /* Everything else... */ - -#define REDIS_REPLY_STRING 1 -#define REDIS_REPLY_ARRAY 2 -#define REDIS_REPLY_INTEGER 3 -#define REDIS_REPLY_NIL 4 -#define REDIS_REPLY_STATUS 5 -#define REDIS_REPLY_ERROR 6 - -#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct redisReadTask { - int type; - int elements; /* number of elements in multibulk container */ - int idx; /* index in parent (array) object */ - void *obj; /* holds user-generated value for a read task */ - struct redisReadTask *parent; /* parent task */ - void *privdata; /* user-settable arbitrary field */ -} redisReadTask; - -typedef struct redisReplyObjectFunctions { - void *(*createString)(const redisReadTask*, char*, size_t); - void *(*createArray)(const redisReadTask*, int); - void *(*createInteger)(const redisReadTask*, long long); - void *(*createNil)(const redisReadTask*); - void (*freeObject)(void*); -} redisReplyObjectFunctions; - -typedef struct redisReader { - int err; /* Error flags, 0 when there is no error */ - char errstr[128]; /* String representation of error when applicable */ - - char *buf; /* Read buffer */ - size_t pos; /* Buffer cursor */ - size_t len; /* Buffer length */ - size_t maxbuf; /* Max length of unused buffer */ - - redisReadTask rstack[9]; - int ridx; /* Index of current read task */ - void *reply; /* Temporary reply pointer */ - - redisReplyObjectFunctions *fn; - void *privdata; -} redisReader; - -/* Public API for the protocol parser. */ -redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn); -void redisReaderFree(redisReader *r); -int redisReaderFeed(redisReader *r, const char *buf, size_t len); -int redisReaderGetReply(redisReader *r, void **reply); - -#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p)) -#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply) -#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/ext/hiredis-0.14.1/include/hiredis/sds.h b/ext/hiredis-0.14.1/include/hiredis/sds.h deleted file mode 100644 index 13be75a9f..000000000 --- a/ext/hiredis-0.14.1/include/hiredis/sds.h +++ /dev/null @@ -1,273 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __SDS_H -#define __SDS_H - -#define SDS_MAX_PREALLOC (1024*1024) - -#include -#include -#include - -typedef char *sds; - -/* Note: sdshdr5 is never used, we just access the flags byte directly. - * However is here to document the layout of type 5 SDS strings. */ -struct __attribute__ ((__packed__)) sdshdr5 { - unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr8 { - uint8_t len; /* used */ - uint8_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr16 { - uint16_t len; /* used */ - uint16_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr32 { - uint32_t len; /* used */ - uint32_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr64 { - uint64_t len; /* used */ - uint64_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; - -#define SDS_TYPE_5 0 -#define SDS_TYPE_8 1 -#define SDS_TYPE_16 2 -#define SDS_TYPE_32 3 -#define SDS_TYPE_64 4 -#define SDS_TYPE_MASK 7 -#define SDS_TYPE_BITS 3 -#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))); -#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) -#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) - -static inline size_t sdslen(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return SDS_TYPE_5_LEN(flags); - case SDS_TYPE_8: - return SDS_HDR(8,s)->len; - case SDS_TYPE_16: - return SDS_HDR(16,s)->len; - case SDS_TYPE_32: - return SDS_HDR(32,s)->len; - case SDS_TYPE_64: - return SDS_HDR(64,s)->len; - } - return 0; -} - -static inline size_t sdsavail(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: { - return 0; - } - case SDS_TYPE_8: { - SDS_HDR_VAR(8,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_16: { - SDS_HDR_VAR(16,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_32: { - SDS_HDR_VAR(32,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_64: { - SDS_HDR_VAR(64,s); - return sh->alloc - sh->len; - } - } - return 0; -} - -static inline void sdssetlen(sds s, size_t newlen) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - { - unsigned char *fp = ((unsigned char*)s)-1; - *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); - } - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->len = newlen; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->len = newlen; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->len = newlen; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->len = newlen; - break; - } -} - -static inline void sdsinclen(sds s, size_t inc) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - { - unsigned char *fp = ((unsigned char*)s)-1; - unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc; - *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); - } - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->len += inc; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->len += inc; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->len += inc; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->len += inc; - break; - } -} - -/* sdsalloc() = sdsavail() + sdslen() */ -static inline size_t sdsalloc(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return SDS_TYPE_5_LEN(flags); - case SDS_TYPE_8: - return SDS_HDR(8,s)->alloc; - case SDS_TYPE_16: - return SDS_HDR(16,s)->alloc; - case SDS_TYPE_32: - return SDS_HDR(32,s)->alloc; - case SDS_TYPE_64: - return SDS_HDR(64,s)->alloc; - } - return 0; -} - -static inline void sdssetalloc(sds s, size_t newlen) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - /* Nothing to do, this type has no total allocation info. */ - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->alloc = newlen; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->alloc = newlen; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->alloc = newlen; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->alloc = newlen; - break; - } -} - -sds sdsnewlen(const void *init, size_t initlen); -sds sdsnew(const char *init); -sds sdsempty(void); -sds sdsdup(const sds s); -void sdsfree(sds s); -sds sdsgrowzero(sds s, size_t len); -sds sdscatlen(sds s, const void *t, size_t len); -sds sdscat(sds s, const char *t); -sds sdscatsds(sds s, const sds t); -sds sdscpylen(sds s, const char *t, size_t len); -sds sdscpy(sds s, const char *t); - -sds sdscatvprintf(sds s, const char *fmt, va_list ap); -#ifdef __GNUC__ -sds sdscatprintf(sds s, const char *fmt, ...) - __attribute__((format(printf, 2, 3))); -#else -sds sdscatprintf(sds s, const char *fmt, ...); -#endif - -sds sdscatfmt(sds s, char const *fmt, ...); -sds sdstrim(sds s, const char *cset); -void sdsrange(sds s, int start, int end); -void sdsupdatelen(sds s); -void sdsclear(sds s); -int sdscmp(const sds s1, const sds s2); -sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); -void sdsfreesplitres(sds *tokens, int count); -void sdstolower(sds s); -void sdstoupper(sds s); -sds sdsfromlonglong(long long value); -sds sdscatrepr(sds s, const char *p, size_t len); -sds *sdssplitargs(const char *line, int *argc); -sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); -sds sdsjoin(char **argv, int argc, char *sep); -sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); - -/* Low level functions exposed to the user API */ -sds sdsMakeRoomFor(sds s, size_t addlen); -void sdsIncrLen(sds s, int incr); -sds sdsRemoveFreeSpace(sds s); -size_t sdsAllocSize(sds s); -void *sdsAllocPtr(sds s); - -/* Export the allocator used by SDS to the program using SDS. - * Sometimes the program SDS is linked to, may use a different set of - * allocators, but may want to allocate or free things that SDS will - * respectively free or allocate. */ -void *sds_malloc(size_t size); -void *sds_realloc(void *ptr, size_t size); -void sds_free(void *ptr); - -#ifdef REDIS_TEST -int sdsTest(int argc, char *argv[]); -#endif - -#endif diff --git a/ext/hiredis-0.14.1/include/hiredis/sdsalloc.h b/ext/hiredis-0.14.1/include/hiredis/sdsalloc.h deleted file mode 100644 index f43023c48..000000000 --- a/ext/hiredis-0.14.1/include/hiredis/sdsalloc.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/* SDS allocator selection. - * - * This file is used in order to change the SDS allocator at compile time. - * Just define the following defines to what you want to use. Also add - * the include of your alternate allocator if needed (not needed in order - * to use the default libc allocator). */ - -#define s_malloc malloc -#define s_realloc realloc -#define s_free free diff --git a/ext/hiredis-0.14.1/include/hiredis/win32.h b/ext/hiredis-0.14.1/include/hiredis/win32.h deleted file mode 100644 index 1a27c18f2..000000000 --- a/ext/hiredis-0.14.1/include/hiredis/win32.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _WIN32_HELPER_INCLUDE -#define _WIN32_HELPER_INCLUDE -#ifdef _MSC_VER - -#ifndef inline -#define inline __inline -#endif - -#ifndef va_copy -#define va_copy(d,s) ((d) = (s)) -#endif - -#ifndef snprintf -#define snprintf c99_snprintf - -__inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) -{ - int count = -1; - - if (size != 0) - count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); - if (count == -1) - count = _vscprintf(format, ap); - - return count; -} - -__inline int c99_snprintf(char* str, size_t size, const char* format, ...) -{ - int count; - va_list ap; - - va_start(ap, format); - count = c99_vsnprintf(str, size, format, ap); - va_end(ap); - - return count; -} -#endif - -#endif -#endif \ No newline at end of file diff --git a/ext/hiredis-0.14.1/lib/centos8/libhiredis.a b/ext/hiredis-0.14.1/lib/centos8/libhiredis.a deleted file mode 100644 index 0b9638798..000000000 Binary files a/ext/hiredis-0.14.1/lib/centos8/libhiredis.a and /dev/null differ diff --git a/ext/hiredis-0.14.1/lib/macos/libhiredis.a b/ext/hiredis-0.14.1/lib/macos/libhiredis.a deleted file mode 100644 index 02bdb8808..000000000 Binary files a/ext/hiredis-0.14.1/lib/macos/libhiredis.a and /dev/null differ diff --git a/ext/hiredis-0.14.1/net.c b/ext/hiredis-0.14.1/net.c deleted file mode 100644 index d71bbcd57..000000000 --- a/ext/hiredis-0.14.1/net.c +++ /dev/null @@ -1,477 +0,0 @@ -/* Extracted from anet.c to work properly with Hiredis error reporting. - * - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2014, Pieter Noordhuis - * Copyright (c) 2015, Matt Stancliff , - * Jan-Erik Rediger - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "fmacros.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "net.h" -#include "sds.h" - -/* Defined in hiredis.c */ -void __redisSetError(redisContext *c, int type, const char *str); - -static void redisContextCloseFd(redisContext *c) { - if (c && c->fd >= 0) { - close(c->fd); - c->fd = -1; - } -} - -static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) { - int errorno = errno; /* snprintf() may change errno */ - char buf[128] = { 0 }; - size_t len = 0; - - if (prefix != NULL) - len = snprintf(buf,sizeof(buf),"%s: ",prefix); - strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len); - __redisSetError(c,type,buf); -} - -static int redisSetReuseAddr(redisContext *c) { - int on = 1; - if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - redisContextCloseFd(c); - return REDIS_ERR; - } - return REDIS_OK; -} - -static int redisCreateSocket(redisContext *c, int type) { - int s; - if ((s = socket(type, SOCK_STREAM, 0)) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - return REDIS_ERR; - } - c->fd = s; - if (type == AF_INET) { - if (redisSetReuseAddr(c) == REDIS_ERR) { - return REDIS_ERR; - } - } - return REDIS_OK; -} - -static int redisSetBlocking(redisContext *c, int blocking) { - int flags; - - /* Set the socket nonblocking. - * Note that fcntl(2) for F_GETFL and F_SETFL can't be - * interrupted by a signal. */ - if ((flags = fcntl(c->fd, F_GETFL)) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)"); - redisContextCloseFd(c); - return REDIS_ERR; - } - - if (blocking) - flags &= ~O_NONBLOCK; - else - flags |= O_NONBLOCK; - - if (fcntl(c->fd, F_SETFL, flags) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)"); - redisContextCloseFd(c); - return REDIS_ERR; - } - return REDIS_OK; -} - -int redisKeepAlive(redisContext *c, int interval) { - int val = 1; - int fd = c->fd; - - if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){ - __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); - return REDIS_ERR; - } - - val = interval; - -#if defined(__APPLE__) && defined(__MACH__) - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) { - __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); - return REDIS_ERR; - } -#else -#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__) - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) { - __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); - return REDIS_ERR; - } - - val = interval/3; - if (val == 0) val = 1; - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) { - __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); - return REDIS_ERR; - } - - val = 3; - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) { - __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); - return REDIS_ERR; - } -#endif -#endif - - return REDIS_OK; -} - -static int redisSetTcpNoDelay(redisContext *c) { - int yes = 1; - if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)"); - redisContextCloseFd(c); - return REDIS_ERR; - } - return REDIS_OK; -} - -#define __MAX_MSEC (((LONG_MAX) - 999) / 1000) - -static int redisContextTimeoutMsec(redisContext *c, long *result) -{ - const struct timeval *timeout = c->timeout; - long msec = -1; - - /* Only use timeout when not NULL. */ - if (timeout != NULL) { - if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) { - *result = msec; - return REDIS_ERR; - } - - msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000); - - if (msec < 0 || msec > INT_MAX) { - msec = INT_MAX; - } - } - - *result = msec; - return REDIS_OK; -} - -static int redisContextWaitReady(redisContext *c, long msec) { - struct pollfd wfd[1]; - - wfd[0].fd = c->fd; - wfd[0].events = POLLOUT; - - if (errno == EINPROGRESS) { - int res; - - if ((res = poll(wfd, 1, msec)) == -1) { - __redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)"); - redisContextCloseFd(c); - return REDIS_ERR; - } else if (res == 0) { - errno = ETIMEDOUT; - __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - redisContextCloseFd(c); - return REDIS_ERR; - } - - if (redisCheckSocketError(c) != REDIS_OK) - return REDIS_ERR; - - return REDIS_OK; - } - - __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - redisContextCloseFd(c); - return REDIS_ERR; -} - -int redisCheckSocketError(redisContext *c) { - int err = 0; - socklen_t errlen = sizeof(err); - - if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)"); - return REDIS_ERR; - } - - if (err) { - errno = err; - __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - return REDIS_ERR; - } - - return REDIS_OK; -} - -int redisContextSetTimeout(redisContext *c, const struct timeval tv) { - if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)"); - return REDIS_ERR; - } - if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)"); - return REDIS_ERR; - } - return REDIS_OK; -} - -static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, - const struct timeval *timeout, - const char *source_addr) { - int s, rv, n; - char _port[6]; /* strlen("65535"); */ - struct addrinfo hints, *servinfo, *bservinfo, *p, *b; - int blocking = (c->flags & REDIS_BLOCK); - int reuseaddr = (c->flags & REDIS_REUSEADDR); - int reuses = 0; - long timeout_msec = -1; - - servinfo = NULL; - c->connection_type = REDIS_CONN_TCP; - c->tcp.port = port; - - /* We need to take possession of the passed parameters - * to make them reusable for a reconnect. - * We also carefully check we don't free data we already own, - * as in the case of the reconnect method. - * - * This is a bit ugly, but atleast it works and doesn't leak memory. - **/ - if (c->tcp.host != addr) { - free(c->tcp.host); - - c->tcp.host = hi_strdup(addr); - } - - if (timeout) { - if (c->timeout != timeout) { - if (c->timeout == NULL) - c->timeout = hi_malloc(sizeof(struct timeval)); - - memcpy(c->timeout, timeout, sizeof(struct timeval)); - } - } else { - free(c->timeout); - c->timeout = NULL; - } - - if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) { - __redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified"); - goto error; - } - - if (source_addr == NULL) { - free(c->tcp.source_addr); - c->tcp.source_addr = NULL; - } else if (c->tcp.source_addr != source_addr) { - free(c->tcp.source_addr); - c->tcp.source_addr = hi_strdup(source_addr); - } - - snprintf(_port, 6, "%d", port); - memset(&hints,0,sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - - /* Try with IPv6 if no IPv4 address was found. We do it in this order since - * in a Redis client you can't afford to test if you have IPv6 connectivity - * as this would add latency to every connect. Otherwise a more sensible - * route could be: Use IPv6 if both addresses are available and there is IPv6 - * connectivity. */ - if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) { - hints.ai_family = AF_INET6; - if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) { - __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv)); - return REDIS_ERR; - } - } - for (p = servinfo; p != NULL; p = p->ai_next) { -addrretry: - if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1) - continue; - - c->fd = s; - if (redisSetBlocking(c,0) != REDIS_OK) - goto error; - if (c->tcp.source_addr) { - int bound = 0; - /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */ - if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) { - char buf[128]; - snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv)); - __redisSetError(c,REDIS_ERR_OTHER,buf); - goto error; - } - - if (reuseaddr) { - n = 1; - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n, - sizeof(n)) < 0) { - freeaddrinfo(bservinfo); - goto error; - } - } - - for (b = bservinfo; b != NULL; b = b->ai_next) { - if (bind(s,b->ai_addr,b->ai_addrlen) != -1) { - bound = 1; - break; - } - } - freeaddrinfo(bservinfo); - if (!bound) { - char buf[128]; - snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno)); - __redisSetError(c,REDIS_ERR_OTHER,buf); - goto error; - } - } - if (connect(s,p->ai_addr,p->ai_addrlen) == -1) { - if (errno == EHOSTUNREACH) { - redisContextCloseFd(c); - continue; - } else if (errno == EINPROGRESS && !blocking) { - /* This is ok. */ - } else if (errno == EADDRNOTAVAIL && reuseaddr) { - if (++reuses >= REDIS_CONNECT_RETRIES) { - goto error; - } else { - redisContextCloseFd(c); - goto addrretry; - } - } else { - if (redisContextWaitReady(c,timeout_msec) != REDIS_OK) - goto error; - } - } - if (blocking && redisSetBlocking(c,1) != REDIS_OK) - goto error; - if (redisSetTcpNoDelay(c) != REDIS_OK) - goto error; - - c->flags |= REDIS_CONNECTED; - rv = REDIS_OK; - goto end; - } - if (p == NULL) { - char buf[128]; - snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno)); - __redisSetError(c,REDIS_ERR_OTHER,buf); - goto error; - } - -error: - rv = REDIS_ERR; -end: - if(servinfo) { - freeaddrinfo(servinfo); - } - - return rv; // Need to return REDIS_OK if alright -} - -int redisContextConnectTcp(redisContext *c, const char *addr, int port, - const struct timeval *timeout) { - return _redisContextConnectTcp(c, addr, port, timeout, NULL); -} - -int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, - const struct timeval *timeout, - const char *source_addr) { - return _redisContextConnectTcp(c, addr, port, timeout, source_addr); -} - -int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) { - int blocking = (c->flags & REDIS_BLOCK); - struct sockaddr_un sa; - long timeout_msec = -1; - - if (redisCreateSocket(c,AF_UNIX) < 0) - return REDIS_ERR; - if (redisSetBlocking(c,0) != REDIS_OK) - return REDIS_ERR; - - c->connection_type = REDIS_CONN_UNIX; - if (c->unix_sock.path != path) - c->unix_sock.path = hi_strdup(path); - - if (timeout) { - if (c->timeout != timeout) { - if (c->timeout == NULL) - c->timeout = hi_malloc(sizeof(struct timeval)); - - memcpy(c->timeout, timeout, sizeof(struct timeval)); - } - } else { - free(c->timeout); - c->timeout = NULL; - } - - if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK) - return REDIS_ERR; - - sa.sun_family = AF_UNIX; - strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); - if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) { - if (errno == EINPROGRESS && !blocking) { - /* This is ok. */ - } else { - if (redisContextWaitReady(c,timeout_msec) != REDIS_OK) - return REDIS_ERR; - } - } - - /* Reset socket to be blocking after connect(2). */ - if (blocking && redisSetBlocking(c,1) != REDIS_OK) - return REDIS_ERR; - - c->flags |= REDIS_CONNECTED; - return REDIS_OK; -} diff --git a/ext/hiredis-0.14.1/net.h b/ext/hiredis-0.14.1/net.h deleted file mode 100644 index d9dc36257..000000000 --- a/ext/hiredis-0.14.1/net.h +++ /dev/null @@ -1,49 +0,0 @@ -/* Extracted from anet.c to work properly with Hiredis error reporting. - * - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2014, Pieter Noordhuis - * Copyright (c) 2015, Matt Stancliff , - * Jan-Erik Rediger - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __NET_H -#define __NET_H - -#include "hiredis.h" - -int redisCheckSocketError(redisContext *c); -int redisContextSetTimeout(redisContext *c, const struct timeval tv); -int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); -int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, - const struct timeval *timeout, - const char *source_addr); -int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout); -int redisKeepAlive(redisContext *c, int interval); - -#endif diff --git a/ext/hiredis-0.14.1/read.c b/ext/hiredis-0.14.1/read.c deleted file mode 100644 index cc2126778..000000000 --- a/ext/hiredis-0.14.1/read.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - - -#include "fmacros.h" -#include -#include -#ifndef _MSC_VER -#include -#endif -#include -#include -#include -#include - -#include "read.h" -#include "sds.h" - -static void __redisReaderSetError(redisReader *r, int type, const char *str) { - size_t len; - - if (r->reply != NULL && r->fn && r->fn->freeObject) { - r->fn->freeObject(r->reply); - r->reply = NULL; - } - - /* Clear input buffer on errors. */ - sdsfree(r->buf); - r->buf = NULL; - r->pos = r->len = 0; - - /* Reset task stack. */ - r->ridx = -1; - - /* Set error. */ - r->err = type; - len = strlen(str); - len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1); - memcpy(r->errstr,str,len); - r->errstr[len] = '\0'; -} - -static size_t chrtos(char *buf, size_t size, char byte) { - size_t len = 0; - - switch(byte) { - case '\\': - case '"': - len = snprintf(buf,size,"\"\\%c\"",byte); - break; - case '\n': len = snprintf(buf,size,"\"\\n\""); break; - case '\r': len = snprintf(buf,size,"\"\\r\""); break; - case '\t': len = snprintf(buf,size,"\"\\t\""); break; - case '\a': len = snprintf(buf,size,"\"\\a\""); break; - case '\b': len = snprintf(buf,size,"\"\\b\""); break; - default: - if (isprint(byte)) - len = snprintf(buf,size,"\"%c\"",byte); - else - len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte); - break; - } - - return len; -} - -static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) { - char cbuf[8], sbuf[128]; - - chrtos(cbuf,sizeof(cbuf),byte); - snprintf(sbuf,sizeof(sbuf), - "Protocol error, got %s as reply type byte", cbuf); - __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf); -} - -static void __redisReaderSetErrorOOM(redisReader *r) { - __redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory"); -} - -static char *readBytes(redisReader *r, unsigned int bytes) { - char *p; - if (r->len-r->pos >= bytes) { - p = r->buf+r->pos; - r->pos += bytes; - return p; - } - return NULL; -} - -/* Find pointer to \r\n. */ -static char *seekNewline(char *s, size_t len) { - int pos = 0; - int _len = len-1; - - /* Position should be < len-1 because the character at "pos" should be - * followed by a \n. Note that strchr cannot be used because it doesn't - * allow to search a limited length and the buffer that is being searched - * might not have a trailing NULL character. */ - while (pos < _len) { - while(pos < _len && s[pos] != '\r') pos++; - if (pos==_len) { - /* Not found. */ - return NULL; - } else { - if (s[pos+1] == '\n') { - /* Found. */ - return s+pos; - } else { - /* Continue searching. */ - pos++; - } - } - } - return NULL; -} - -/* Convert a string into a long long. Returns REDIS_OK if the string could be - * parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value - * will be set to the parsed value when appropriate. - * - * Note that this function demands that the string strictly represents - * a long long: no spaces or other characters before or after the string - * representing the number are accepted, nor zeroes at the start if not - * for the string "0" representing the zero number. - * - * Because of its strictness, it is safe to use this function to check if - * you can convert a string into a long long, and obtain back the string - * from the number without any loss in the string representation. */ -static int string2ll(const char *s, size_t slen, long long *value) { - const char *p = s; - size_t plen = 0; - int negative = 0; - unsigned long long v; - - if (plen == slen) - return REDIS_ERR; - - /* Special case: first and only digit is 0. */ - if (slen == 1 && p[0] == '0') { - if (value != NULL) *value = 0; - return REDIS_OK; - } - - if (p[0] == '-') { - negative = 1; - p++; plen++; - - /* Abort on only a negative sign. */ - if (plen == slen) - return REDIS_ERR; - } - - /* First digit should be 1-9, otherwise the string should just be 0. */ - if (p[0] >= '1' && p[0] <= '9') { - v = p[0]-'0'; - p++; plen++; - } else if (p[0] == '0' && slen == 1) { - *value = 0; - return REDIS_OK; - } else { - return REDIS_ERR; - } - - while (plen < slen && p[0] >= '0' && p[0] <= '9') { - if (v > (ULLONG_MAX / 10)) /* Overflow. */ - return REDIS_ERR; - v *= 10; - - if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */ - return REDIS_ERR; - v += p[0]-'0'; - - p++; plen++; - } - - /* Return if not all bytes were used. */ - if (plen < slen) - return REDIS_ERR; - - if (negative) { - if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */ - return REDIS_ERR; - if (value != NULL) *value = -v; - } else { - if (v > LLONG_MAX) /* Overflow. */ - return REDIS_ERR; - if (value != NULL) *value = v; - } - return REDIS_OK; -} - -static char *readLine(redisReader *r, int *_len) { - char *p, *s; - int len; - - p = r->buf+r->pos; - s = seekNewline(p,(r->len-r->pos)); - if (s != NULL) { - len = s-(r->buf+r->pos); - r->pos += len+2; /* skip \r\n */ - if (_len) *_len = len; - return p; - } - return NULL; -} - -static void moveToNextTask(redisReader *r) { - redisReadTask *cur, *prv; - while (r->ridx >= 0) { - /* Return a.s.a.p. when the stack is now empty. */ - if (r->ridx == 0) { - r->ridx--; - return; - } - - cur = &(r->rstack[r->ridx]); - prv = &(r->rstack[r->ridx-1]); - assert(prv->type == REDIS_REPLY_ARRAY); - if (cur->idx == prv->elements-1) { - r->ridx--; - } else { - /* Reset the type because the next item can be anything */ - assert(cur->idx < prv->elements); - cur->type = -1; - cur->elements = -1; - cur->idx++; - return; - } - } -} - -static int processLineItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); - void *obj; - char *p; - int len; - - if ((p = readLine(r,&len)) != NULL) { - if (cur->type == REDIS_REPLY_INTEGER) { - if (r->fn && r->fn->createInteger) { - long long v; - if (string2ll(p, len, &v) == REDIS_ERR) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Bad integer value"); - return REDIS_ERR; - } - obj = r->fn->createInteger(cur,v); - } else { - obj = (void*)REDIS_REPLY_INTEGER; - } - } else { - /* Type will be error or status. */ - if (r->fn && r->fn->createString) - obj = r->fn->createString(cur,p,len); - else - obj = (void*)(size_t)(cur->type); - } - - if (obj == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } - - /* Set reply if this is the root object. */ - if (r->ridx == 0) r->reply = obj; - moveToNextTask(r); - return REDIS_OK; - } - - return REDIS_ERR; -} - -static int processBulkItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); - void *obj = NULL; - char *p, *s; - long long len; - unsigned long bytelen; - int success = 0; - - p = r->buf+r->pos; - s = seekNewline(p,r->len-r->pos); - if (s != NULL) { - p = r->buf+r->pos; - bytelen = s-(r->buf+r->pos)+2; /* include \r\n */ - - if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Bad bulk string length"); - return REDIS_ERR; - } - - if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Bulk string length out of range"); - return REDIS_ERR; - } - - if (len == -1) { - /* The nil object can always be created. */ - if (r->fn && r->fn->createNil) - obj = r->fn->createNil(cur); - else - obj = (void*)REDIS_REPLY_NIL; - success = 1; - } else { - /* Only continue when the buffer contains the entire bulk item. */ - bytelen += len+2; /* include \r\n */ - if (r->pos+bytelen <= r->len) { - if (r->fn && r->fn->createString) - obj = r->fn->createString(cur,s+2,len); - else - obj = (void*)REDIS_REPLY_STRING; - success = 1; - } - } - - /* Proceed when obj was created. */ - if (success) { - if (obj == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } - - r->pos += bytelen; - - /* Set reply if this is the root object. */ - if (r->ridx == 0) r->reply = obj; - moveToNextTask(r); - return REDIS_OK; - } - } - - return REDIS_ERR; -} - -static int processMultiBulkItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); - void *obj; - char *p; - long long elements; - int root = 0, len; - - /* Set error for nested multi bulks with depth > 7 */ - if (r->ridx == 8) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "No support for nested multi bulk replies with depth > 7"); - return REDIS_ERR; - } - - if ((p = readLine(r,&len)) != NULL) { - if (string2ll(p, len, &elements) == REDIS_ERR) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Bad multi-bulk length"); - return REDIS_ERR; - } - - root = (r->ridx == 0); - - if (elements < -1 || elements > INT_MAX) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Multi-bulk length out of range"); - return REDIS_ERR; - } - - if (elements == -1) { - if (r->fn && r->fn->createNil) - obj = r->fn->createNil(cur); - else - obj = (void*)REDIS_REPLY_NIL; - - if (obj == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } - - moveToNextTask(r); - } else { - if (r->fn && r->fn->createArray) - obj = r->fn->createArray(cur,elements); - else - obj = (void*)REDIS_REPLY_ARRAY; - - if (obj == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } - - /* Modify task stack when there are more than 0 elements. */ - if (elements > 0) { - cur->elements = elements; - cur->obj = obj; - r->ridx++; - r->rstack[r->ridx].type = -1; - r->rstack[r->ridx].elements = -1; - r->rstack[r->ridx].idx = 0; - r->rstack[r->ridx].obj = NULL; - r->rstack[r->ridx].parent = cur; - r->rstack[r->ridx].privdata = r->privdata; - } else { - moveToNextTask(r); - } - } - - /* Set reply if this is the root object. */ - if (root) r->reply = obj; - return REDIS_OK; - } - - return REDIS_ERR; -} - -static int processItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); - char *p; - - /* check if we need to read type */ - if (cur->type < 0) { - if ((p = readBytes(r,1)) != NULL) { - switch (p[0]) { - case '-': - cur->type = REDIS_REPLY_ERROR; - break; - case '+': - cur->type = REDIS_REPLY_STATUS; - break; - case ':': - cur->type = REDIS_REPLY_INTEGER; - break; - case '$': - cur->type = REDIS_REPLY_STRING; - break; - case '*': - cur->type = REDIS_REPLY_ARRAY; - break; - default: - __redisReaderSetErrorProtocolByte(r,*p); - return REDIS_ERR; - } - } else { - /* could not consume 1 byte */ - return REDIS_ERR; - } - } - - /* process typed item */ - switch(cur->type) { - case REDIS_REPLY_ERROR: - case REDIS_REPLY_STATUS: - case REDIS_REPLY_INTEGER: - return processLineItem(r); - case REDIS_REPLY_STRING: - return processBulkItem(r); - case REDIS_REPLY_ARRAY: - return processMultiBulkItem(r); - default: - assert(NULL); - return REDIS_ERR; /* Avoid warning. */ - } -} - -redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) { - redisReader *r; - - r = calloc(1,sizeof(redisReader)); - if (r == NULL) - return NULL; - - r->fn = fn; - r->buf = sdsempty(); - r->maxbuf = REDIS_READER_MAX_BUF; - if (r->buf == NULL) { - free(r); - return NULL; - } - - r->ridx = -1; - return r; -} - -void redisReaderFree(redisReader *r) { - if (r == NULL) - return; - if (r->reply != NULL && r->fn && r->fn->freeObject) - r->fn->freeObject(r->reply); - sdsfree(r->buf); - free(r); -} - -int redisReaderFeed(redisReader *r, const char *buf, size_t len) { - sds newbuf; - - /* Return early when this reader is in an erroneous state. */ - if (r->err) - return REDIS_ERR; - - /* Copy the provided buffer. */ - if (buf != NULL && len >= 1) { - /* Destroy internal buffer when it is empty and is quite large. */ - if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) { - sdsfree(r->buf); - r->buf = sdsempty(); - r->pos = 0; - - /* r->buf should not be NULL since we just free'd a larger one. */ - assert(r->buf != NULL); - } - - newbuf = sdscatlen(r->buf,buf,len); - if (newbuf == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } - - r->buf = newbuf; - r->len = sdslen(r->buf); - } - - return REDIS_OK; -} - -int redisReaderGetReply(redisReader *r, void **reply) { - /* Default target pointer to NULL. */ - if (reply != NULL) - *reply = NULL; - - /* Return early when this reader is in an erroneous state. */ - if (r->err) - return REDIS_ERR; - - /* When the buffer is empty, there will never be a reply. */ - if (r->len == 0) - return REDIS_OK; - - /* Set first item to process when the stack is empty. */ - if (r->ridx == -1) { - r->rstack[0].type = -1; - r->rstack[0].elements = -1; - r->rstack[0].idx = -1; - r->rstack[0].obj = NULL; - r->rstack[0].parent = NULL; - r->rstack[0].privdata = r->privdata; - r->ridx = 0; - } - - /* Process items in reply. */ - while (r->ridx >= 0) - if (processItem(r) != REDIS_OK) - break; - - /* Return ASAP when an error occurred. */ - if (r->err) - return REDIS_ERR; - - /* Discard part of the buffer when we've consumed at least 1k, to avoid - * doing unnecessary calls to memmove() in sds.c. */ - if (r->pos >= 1024) { - sdsrange(r->buf,r->pos,-1); - r->pos = 0; - r->len = sdslen(r->buf); - } - - /* Emit a reply when there is one. */ - if (r->ridx == -1) { - if (reply != NULL) - *reply = r->reply; - r->reply = NULL; - } - return REDIS_OK; -} diff --git a/ext/hiredis-0.14.1/read.h b/ext/hiredis-0.14.1/read.h deleted file mode 100644 index 2988aa453..000000000 --- a/ext/hiredis-0.14.1/read.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - - -#ifndef __HIREDIS_READ_H -#define __HIREDIS_READ_H -#include /* for size_t */ - -#define REDIS_ERR -1 -#define REDIS_OK 0 - -/* When an error occurs, the err flag in a context is set to hold the type of - * error that occurred. REDIS_ERR_IO means there was an I/O error and you - * should use the "errno" variable to find out what is wrong. - * For other values, the "errstr" field will hold a description. */ -#define REDIS_ERR_IO 1 /* Error in read or write */ -#define REDIS_ERR_EOF 3 /* End of file */ -#define REDIS_ERR_PROTOCOL 4 /* Protocol error */ -#define REDIS_ERR_OOM 5 /* Out of memory */ -#define REDIS_ERR_OTHER 2 /* Everything else... */ - -#define REDIS_REPLY_STRING 1 -#define REDIS_REPLY_ARRAY 2 -#define REDIS_REPLY_INTEGER 3 -#define REDIS_REPLY_NIL 4 -#define REDIS_REPLY_STATUS 5 -#define REDIS_REPLY_ERROR 6 - -#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct redisReadTask { - int type; - int elements; /* number of elements in multibulk container */ - int idx; /* index in parent (array) object */ - void *obj; /* holds user-generated value for a read task */ - struct redisReadTask *parent; /* parent task */ - void *privdata; /* user-settable arbitrary field */ -} redisReadTask; - -typedef struct redisReplyObjectFunctions { - void *(*createString)(const redisReadTask*, char*, size_t); - void *(*createArray)(const redisReadTask*, int); - void *(*createInteger)(const redisReadTask*, long long); - void *(*createNil)(const redisReadTask*); - void (*freeObject)(void*); -} redisReplyObjectFunctions; - -typedef struct redisReader { - int err; /* Error flags, 0 when there is no error */ - char errstr[128]; /* String representation of error when applicable */ - - char *buf; /* Read buffer */ - size_t pos; /* Buffer cursor */ - size_t len; /* Buffer length */ - size_t maxbuf; /* Max length of unused buffer */ - - redisReadTask rstack[9]; - int ridx; /* Index of current read task */ - void *reply; /* Temporary reply pointer */ - - redisReplyObjectFunctions *fn; - void *privdata; -} redisReader; - -/* Public API for the protocol parser. */ -redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn); -void redisReaderFree(redisReader *r); -int redisReaderFeed(redisReader *r, const char *buf, size_t len); -int redisReaderGetReply(redisReader *r, void **reply); - -#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p)) -#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply) -#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/ext/hiredis-0.14.1/sds.c b/ext/hiredis-0.14.1/sds.c deleted file mode 100644 index 923ffd82f..000000000 --- a/ext/hiredis-0.14.1/sds.c +++ /dev/null @@ -1,1272 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include "sds.h" -#include "sdsalloc.h" - -static inline int sdsHdrSize(char type) { - switch(type&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return sizeof(struct sdshdr5); - case SDS_TYPE_8: - return sizeof(struct sdshdr8); - case SDS_TYPE_16: - return sizeof(struct sdshdr16); - case SDS_TYPE_32: - return sizeof(struct sdshdr32); - case SDS_TYPE_64: - return sizeof(struct sdshdr64); - } - return 0; -} - -static inline char sdsReqType(size_t string_size) { - if (string_size < 32) - return SDS_TYPE_5; - if (string_size < 0xff) - return SDS_TYPE_8; - if (string_size < 0xffff) - return SDS_TYPE_16; - if (string_size < 0xffffffff) - return SDS_TYPE_32; - return SDS_TYPE_64; -} - -/* Create a new sds string with the content specified by the 'init' pointer - * and 'initlen'. - * If NULL is used for 'init' the string is initialized with zero bytes. - * - * The string is always null-termined (all the sds strings are, always) so - * even if you create an sds string with: - * - * mystring = sdsnewlen("abc",3); - * - * You can print the string with printf() as there is an implicit \0 at the - * end of the string. However the string is binary safe and can contain - * \0 characters in the middle, as the length is stored in the sds header. */ -sds sdsnewlen(const void *init, size_t initlen) { - void *sh; - sds s; - char type = sdsReqType(initlen); - /* Empty strings are usually created in order to append. Use type 8 - * since type 5 is not good at this. */ - if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8; - int hdrlen = sdsHdrSize(type); - unsigned char *fp; /* flags pointer. */ - - sh = s_malloc(hdrlen+initlen+1); - if (sh == NULL) return NULL; - if (!init) - memset(sh, 0, hdrlen+initlen+1); - s = (char*)sh+hdrlen; - fp = ((unsigned char*)s)-1; - switch(type) { - case SDS_TYPE_5: { - *fp = type | (initlen << SDS_TYPE_BITS); - break; - } - case SDS_TYPE_8: { - SDS_HDR_VAR(8,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - case SDS_TYPE_16: { - SDS_HDR_VAR(16,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - case SDS_TYPE_32: { - SDS_HDR_VAR(32,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - case SDS_TYPE_64: { - SDS_HDR_VAR(64,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - } - if (initlen && init) - memcpy(s, init, initlen); - s[initlen] = '\0'; - return s; -} - -/* Create an empty (zero length) sds string. Even in this case the string - * always has an implicit null term. */ -sds sdsempty(void) { - return sdsnewlen("",0); -} - -/* Create a new sds string starting from a null terminated C string. */ -sds sdsnew(const char *init) { - size_t initlen = (init == NULL) ? 0 : strlen(init); - return sdsnewlen(init, initlen); -} - -/* Duplicate an sds string. */ -sds sdsdup(const sds s) { - return sdsnewlen(s, sdslen(s)); -} - -/* Free an sds string. No operation is performed if 's' is NULL. */ -void sdsfree(sds s) { - if (s == NULL) return; - s_free((char*)s-sdsHdrSize(s[-1])); -} - -/* Set the sds string length to the length as obtained with strlen(), so - * considering as content only up to the first null term character. - * - * This function is useful when the sds string is hacked manually in some - * way, like in the following example: - * - * s = sdsnew("foobar"); - * s[2] = '\0'; - * sdsupdatelen(s); - * printf("%d\n", sdslen(s)); - * - * The output will be "2", but if we comment out the call to sdsupdatelen() - * the output will be "6" as the string was modified but the logical length - * remains 6 bytes. */ -void sdsupdatelen(sds s) { - int reallen = strlen(s); - sdssetlen(s, reallen); -} - -/* Modify an sds string in-place to make it empty (zero length). - * However all the existing buffer is not discarded but set as free space - * so that next append operations will not require allocations up to the - * number of bytes previously available. */ -void sdsclear(sds s) { - sdssetlen(s, 0); - s[0] = '\0'; -} - -/* Enlarge the free space at the end of the sds string so that the caller - * is sure that after calling this function can overwrite up to addlen - * bytes after the end of the string, plus one more byte for nul term. - * - * Note: this does not change the *length* of the sds string as returned - * by sdslen(), but only the free buffer space we have. */ -sds sdsMakeRoomFor(sds s, size_t addlen) { - void *sh, *newsh; - size_t avail = sdsavail(s); - size_t len, newlen; - char type, oldtype = s[-1] & SDS_TYPE_MASK; - int hdrlen; - - /* Return ASAP if there is enough space left. */ - if (avail >= addlen) return s; - - len = sdslen(s); - sh = (char*)s-sdsHdrSize(oldtype); - newlen = (len+addlen); - if (newlen < SDS_MAX_PREALLOC) - newlen *= 2; - else - newlen += SDS_MAX_PREALLOC; - - type = sdsReqType(newlen); - - /* Don't use type 5: the user is appending to the string and type 5 is - * not able to remember empty space, so sdsMakeRoomFor() must be called - * at every appending operation. */ - if (type == SDS_TYPE_5) type = SDS_TYPE_8; - - hdrlen = sdsHdrSize(type); - if (oldtype==type) { - newsh = s_realloc(sh, hdrlen+newlen+1); - if (newsh == NULL) return NULL; - s = (char*)newsh+hdrlen; - } else { - /* Since the header size changes, need to move the string forward, - * and can't use realloc */ - newsh = s_malloc(hdrlen+newlen+1); - if (newsh == NULL) return NULL; - memcpy((char*)newsh+hdrlen, s, len+1); - s_free(sh); - s = (char*)newsh+hdrlen; - s[-1] = type; - sdssetlen(s, len); - } - sdssetalloc(s, newlen); - return s; -} - -/* Reallocate the sds string so that it has no free space at the end. The - * contained string remains not altered, but next concatenation operations - * will require a reallocation. - * - * After the call, the passed sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdsRemoveFreeSpace(sds s) { - void *sh, *newsh; - char type, oldtype = s[-1] & SDS_TYPE_MASK; - int hdrlen; - size_t len = sdslen(s); - sh = (char*)s-sdsHdrSize(oldtype); - - type = sdsReqType(len); - hdrlen = sdsHdrSize(type); - if (oldtype==type) { - newsh = s_realloc(sh, hdrlen+len+1); - if (newsh == NULL) return NULL; - s = (char*)newsh+hdrlen; - } else { - newsh = s_malloc(hdrlen+len+1); - if (newsh == NULL) return NULL; - memcpy((char*)newsh+hdrlen, s, len+1); - s_free(sh); - s = (char*)newsh+hdrlen; - s[-1] = type; - sdssetlen(s, len); - } - sdssetalloc(s, len); - return s; -} - -/* Return the total size of the allocation of the specifed sds string, - * including: - * 1) The sds header before the pointer. - * 2) The string. - * 3) The free buffer at the end if any. - * 4) The implicit null term. - */ -size_t sdsAllocSize(sds s) { - size_t alloc = sdsalloc(s); - return sdsHdrSize(s[-1])+alloc+1; -} - -/* Return the pointer of the actual SDS allocation (normally SDS strings - * are referenced by the start of the string buffer). */ -void *sdsAllocPtr(sds s) { - return (void*) (s-sdsHdrSize(s[-1])); -} - -/* Increment the sds length and decrements the left free space at the - * end of the string according to 'incr'. Also set the null term - * in the new end of the string. - * - * This function is used in order to fix the string length after the - * user calls sdsMakeRoomFor(), writes something after the end of - * the current string, and finally needs to set the new length. - * - * Note: it is possible to use a negative increment in order to - * right-trim the string. - * - * Usage example: - * - * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the - * following schema, to cat bytes coming from the kernel to the end of an - * sds string without copying into an intermediate buffer: - * - * oldlen = sdslen(s); - * s = sdsMakeRoomFor(s, BUFFER_SIZE); - * nread = read(fd, s+oldlen, BUFFER_SIZE); - * ... check for nread <= 0 and handle it ... - * sdsIncrLen(s, nread); - */ -void sdsIncrLen(sds s, int incr) { - unsigned char flags = s[-1]; - size_t len; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: { - unsigned char *fp = ((unsigned char*)s)-1; - unsigned char oldlen = SDS_TYPE_5_LEN(flags); - assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr))); - *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS); - len = oldlen+incr; - break; - } - case SDS_TYPE_8: { - SDS_HDR_VAR(8,s); - assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); - len = (sh->len += incr); - break; - } - case SDS_TYPE_16: { - SDS_HDR_VAR(16,s); - assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); - len = (sh->len += incr); - break; - } - case SDS_TYPE_32: { - SDS_HDR_VAR(32,s); - assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); - len = (sh->len += incr); - break; - } - case SDS_TYPE_64: { - SDS_HDR_VAR(64,s); - assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr))); - len = (sh->len += incr); - break; - } - default: len = 0; /* Just to avoid compilation warnings. */ - } - s[len] = '\0'; -} - -/* Grow the sds to have the specified length. Bytes that were not part of - * the original length of the sds will be set to zero. - * - * if the specified length is smaller than the current length, no operation - * is performed. */ -sds sdsgrowzero(sds s, size_t len) { - size_t curlen = sdslen(s); - - if (len <= curlen) return s; - s = sdsMakeRoomFor(s,len-curlen); - if (s == NULL) return NULL; - - /* Make sure added region doesn't contain garbage */ - memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ - sdssetlen(s, len); - return s; -} - -/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the - * end of the specified sds string 's'. - * - * After the call, the passed sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscatlen(sds s, const void *t, size_t len) { - size_t curlen = sdslen(s); - - s = sdsMakeRoomFor(s,len); - if (s == NULL) return NULL; - memcpy(s+curlen, t, len); - sdssetlen(s, curlen+len); - s[curlen+len] = '\0'; - return s; -} - -/* Append the specified null termianted C string to the sds string 's'. - * - * After the call, the passed sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscat(sds s, const char *t) { - return sdscatlen(s, t, strlen(t)); -} - -/* Append the specified sds 't' to the existing sds 's'. - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscatsds(sds s, const sds t) { - return sdscatlen(s, t, sdslen(t)); -} - -/* Destructively modify the sds string 's' to hold the specified binary - * safe string pointed by 't' of length 'len' bytes. */ -sds sdscpylen(sds s, const char *t, size_t len) { - if (sdsalloc(s) < len) { - s = sdsMakeRoomFor(s,len-sdslen(s)); - if (s == NULL) return NULL; - } - memcpy(s, t, len); - s[len] = '\0'; - sdssetlen(s, len); - return s; -} - -/* Like sdscpylen() but 't' must be a null-termined string so that the length - * of the string is obtained with strlen(). */ -sds sdscpy(sds s, const char *t) { - return sdscpylen(s, t, strlen(t)); -} - -/* Helper for sdscatlonglong() doing the actual number -> string - * conversion. 's' must point to a string with room for at least - * SDS_LLSTR_SIZE bytes. - * - * The function returns the length of the null-terminated string - * representation stored at 's'. */ -#define SDS_LLSTR_SIZE 21 -int sdsll2str(char *s, long long value) { - char *p, aux; - unsigned long long v; - size_t l; - - /* Generate the string representation, this method produces - * an reversed string. */ - v = (value < 0) ? -value : value; - p = s; - do { - *p++ = '0'+(v%10); - v /= 10; - } while(v); - if (value < 0) *p++ = '-'; - - /* Compute length and add null term. */ - l = p-s; - *p = '\0'; - - /* Reverse the string. */ - p--; - while(s < p) { - aux = *s; - *s = *p; - *p = aux; - s++; - p--; - } - return l; -} - -/* Identical sdsll2str(), but for unsigned long long type. */ -int sdsull2str(char *s, unsigned long long v) { - char *p, aux; - size_t l; - - /* Generate the string representation, this method produces - * an reversed string. */ - p = s; - do { - *p++ = '0'+(v%10); - v /= 10; - } while(v); - - /* Compute length and add null term. */ - l = p-s; - *p = '\0'; - - /* Reverse the string. */ - p--; - while(s < p) { - aux = *s; - *s = *p; - *p = aux; - s++; - p--; - } - return l; -} - -/* Create an sds string from a long long value. It is much faster than: - * - * sdscatprintf(sdsempty(),"%lld\n", value); - */ -sds sdsfromlonglong(long long value) { - char buf[SDS_LLSTR_SIZE]; - int len = sdsll2str(buf,value); - - return sdsnewlen(buf,len); -} - -/* Like sdscatprintf() but gets va_list instead of being variadic. */ -sds sdscatvprintf(sds s, const char *fmt, va_list ap) { - va_list cpy; - char staticbuf[1024], *buf = staticbuf, *t; - size_t buflen = strlen(fmt)*2; - - /* We try to start using a static buffer for speed. - * If not possible we revert to heap allocation. */ - if (buflen > sizeof(staticbuf)) { - buf = s_malloc(buflen); - if (buf == NULL) return NULL; - } else { - buflen = sizeof(staticbuf); - } - - /* Try with buffers two times bigger every time we fail to - * fit the string in the current buffer size. */ - while(1) { - buf[buflen-2] = '\0'; - va_copy(cpy,ap); - vsnprintf(buf, buflen, fmt, cpy); - va_end(cpy); - if (buf[buflen-2] != '\0') { - if (buf != staticbuf) s_free(buf); - buflen *= 2; - buf = s_malloc(buflen); - if (buf == NULL) return NULL; - continue; - } - break; - } - - /* Finally concat the obtained string to the SDS string and return it. */ - t = sdscat(s, buf); - if (buf != staticbuf) s_free(buf); - return t; -} - -/* Append to the sds string 's' a string obtained using printf-alike format - * specifier. - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. - * - * Example: - * - * s = sdsnew("Sum is: "); - * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b). - * - * Often you need to create a string from scratch with the printf-alike - * format. When this is the need, just use sdsempty() as the target string: - * - * s = sdscatprintf(sdsempty(), "... your format ...", args); - */ -sds sdscatprintf(sds s, const char *fmt, ...) { - va_list ap; - char *t; - va_start(ap, fmt); - t = sdscatvprintf(s,fmt,ap); - va_end(ap); - return t; -} - -/* This function is similar to sdscatprintf, but much faster as it does - * not rely on sprintf() family functions implemented by the libc that - * are often very slow. Moreover directly handling the sds string as - * new data is concatenated provides a performance improvement. - * - * However this function only handles an incompatible subset of printf-alike - * format specifiers: - * - * %s - C String - * %S - SDS string - * %i - signed int - * %I - 64 bit signed integer (long long, int64_t) - * %u - unsigned int - * %U - 64 bit unsigned integer (unsigned long long, uint64_t) - * %% - Verbatim "%" character. - */ -sds sdscatfmt(sds s, char const *fmt, ...) { - const char *f = fmt; - int i; - va_list ap; - - va_start(ap,fmt); - i = sdslen(s); /* Position of the next byte to write to dest str. */ - while(*f) { - char next, *str; - size_t l; - long long num; - unsigned long long unum; - - /* Make sure there is always space for at least 1 char. */ - if (sdsavail(s)==0) { - s = sdsMakeRoomFor(s,1); - } - - switch(*f) { - case '%': - next = *(f+1); - f++; - switch(next) { - case 's': - case 'S': - str = va_arg(ap,char*); - l = (next == 's') ? strlen(str) : sdslen(str); - if (sdsavail(s) < l) { - s = sdsMakeRoomFor(s,l); - } - memcpy(s+i,str,l); - sdsinclen(s,l); - i += l; - break; - case 'i': - case 'I': - if (next == 'i') - num = va_arg(ap,int); - else - num = va_arg(ap,long long); - { - char buf[SDS_LLSTR_SIZE]; - l = sdsll2str(buf,num); - if (sdsavail(s) < l) { - s = sdsMakeRoomFor(s,l); - } - memcpy(s+i,buf,l); - sdsinclen(s,l); - i += l; - } - break; - case 'u': - case 'U': - if (next == 'u') - unum = va_arg(ap,unsigned int); - else - unum = va_arg(ap,unsigned long long); - { - char buf[SDS_LLSTR_SIZE]; - l = sdsull2str(buf,unum); - if (sdsavail(s) < l) { - s = sdsMakeRoomFor(s,l); - } - memcpy(s+i,buf,l); - sdsinclen(s,l); - i += l; - } - break; - default: /* Handle %% and generally %. */ - s[i++] = next; - sdsinclen(s,1); - break; - } - break; - default: - s[i++] = *f; - sdsinclen(s,1); - break; - } - f++; - } - va_end(ap); - - /* Add null-term */ - s[i] = '\0'; - return s; -} - -/* Remove the part of the string from left and from right composed just of - * contiguous characters found in 'cset', that is a null terminted C string. - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. - * - * Example: - * - * s = sdsnew("AA...AA.a.aa.aHelloWorld :::"); - * s = sdstrim(s,"Aa. :"); - * printf("%s\n", s); - * - * Output will be just "Hello World". - */ -sds sdstrim(sds s, const char *cset) { - char *start, *end, *sp, *ep; - size_t len; - - sp = start = s; - ep = end = s+sdslen(s)-1; - while(sp <= end && strchr(cset, *sp)) sp++; - while(ep > sp && strchr(cset, *ep)) ep--; - len = (sp > ep) ? 0 : ((ep-sp)+1); - if (s != sp) memmove(s, sp, len); - s[len] = '\0'; - sdssetlen(s,len); - return s; -} - -/* Turn the string into a smaller (or equal) string containing only the - * substring specified by the 'start' and 'end' indexes. - * - * start and end can be negative, where -1 means the last character of the - * string, -2 the penultimate character, and so forth. - * - * The interval is inclusive, so the start and end characters will be part - * of the resulting string. - * - * The string is modified in-place. - * - * Example: - * - * s = sdsnew("Hello World"); - * sdsrange(s,1,-1); => "ello World" - */ -void sdsrange(sds s, int start, int end) { - size_t newlen, len = sdslen(s); - - if (len == 0) return; - if (start < 0) { - start = len+start; - if (start < 0) start = 0; - } - if (end < 0) { - end = len+end; - if (end < 0) end = 0; - } - newlen = (start > end) ? 0 : (end-start)+1; - if (newlen != 0) { - if (start >= (signed)len) { - newlen = 0; - } else if (end >= (signed)len) { - end = len-1; - newlen = (start > end) ? 0 : (end-start)+1; - } - } else { - start = 0; - } - if (start && newlen) memmove(s, s+start, newlen); - s[newlen] = 0; - sdssetlen(s,newlen); -} - -/* Apply tolower() to every character of the sds string 's'. */ -void sdstolower(sds s) { - int len = sdslen(s), j; - - for (j = 0; j < len; j++) s[j] = tolower(s[j]); -} - -/* Apply toupper() to every character of the sds string 's'. */ -void sdstoupper(sds s) { - int len = sdslen(s), j; - - for (j = 0; j < len; j++) s[j] = toupper(s[j]); -} - -/* Compare two sds strings s1 and s2 with memcmp(). - * - * Return value: - * - * positive if s1 > s2. - * negative if s1 < s2. - * 0 if s1 and s2 are exactly the same binary string. - * - * If two strings share exactly the same prefix, but one of the two has - * additional characters, the longer string is considered to be greater than - * the smaller one. */ -int sdscmp(const sds s1, const sds s2) { - size_t l1, l2, minlen; - int cmp; - - l1 = sdslen(s1); - l2 = sdslen(s2); - minlen = (l1 < l2) ? l1 : l2; - cmp = memcmp(s1,s2,minlen); - if (cmp == 0) return l1-l2; - return cmp; -} - -/* Split 's' with separator in 'sep'. An array - * of sds strings is returned. *count will be set - * by reference to the number of tokens returned. - * - * On out of memory, zero length string, zero length - * separator, NULL is returned. - * - * Note that 'sep' is able to split a string using - * a multi-character separator. For example - * sdssplit("foo_-_bar","_-_"); will return two - * elements "foo" and "bar". - * - * This version of the function is binary-safe but - * requires length arguments. sdssplit() is just the - * same function but for zero-terminated strings. - */ -sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) { - int elements = 0, slots = 5, start = 0, j; - sds *tokens; - - if (seplen < 1 || len < 0) return NULL; - - tokens = s_malloc(sizeof(sds)*slots); - if (tokens == NULL) return NULL; - - if (len == 0) { - *count = 0; - return tokens; - } - for (j = 0; j < (len-(seplen-1)); j++) { - /* make sure there is room for the next element and the final one */ - if (slots < elements+2) { - sds *newtokens; - - slots *= 2; - newtokens = s_realloc(tokens,sizeof(sds)*slots); - if (newtokens == NULL) goto cleanup; - tokens = newtokens; - } - /* search the separator */ - if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) { - tokens[elements] = sdsnewlen(s+start,j-start); - if (tokens[elements] == NULL) goto cleanup; - elements++; - start = j+seplen; - j = j+seplen-1; /* skip the separator */ - } - } - /* Add the final element. We are sure there is room in the tokens array. */ - tokens[elements] = sdsnewlen(s+start,len-start); - if (tokens[elements] == NULL) goto cleanup; - elements++; - *count = elements; - return tokens; - -cleanup: - { - int i; - for (i = 0; i < elements; i++) sdsfree(tokens[i]); - s_free(tokens); - *count = 0; - return NULL; - } -} - -/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */ -void sdsfreesplitres(sds *tokens, int count) { - if (!tokens) return; - while(count--) - sdsfree(tokens[count]); - s_free(tokens); -} - -/* Append to the sds string "s" an escaped string representation where - * all the non-printable characters (tested with isprint()) are turned into - * escapes in the form "\n\r\a...." or "\x". - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscatrepr(sds s, const char *p, size_t len) { - s = sdscatlen(s,"\"",1); - while(len--) { - switch(*p) { - case '\\': - case '"': - s = sdscatprintf(s,"\\%c",*p); - break; - case '\n': s = sdscatlen(s,"\\n",2); break; - case '\r': s = sdscatlen(s,"\\r",2); break; - case '\t': s = sdscatlen(s,"\\t",2); break; - case '\a': s = sdscatlen(s,"\\a",2); break; - case '\b': s = sdscatlen(s,"\\b",2); break; - default: - if (isprint(*p)) - s = sdscatprintf(s,"%c",*p); - else - s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); - break; - } - p++; - } - return sdscatlen(s,"\"",1); -} - -/* Helper function for sdssplitargs() that returns non zero if 'c' - * is a valid hex digit. */ -int is_hex_digit(char c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F'); -} - -/* Helper function for sdssplitargs() that converts a hex digit into an - * integer from 0 to 15 */ -int hex_digit_to_int(char c) { - switch(c) { - case '0': return 0; - case '1': return 1; - case '2': return 2; - case '3': return 3; - case '4': return 4; - case '5': return 5; - case '6': return 6; - case '7': return 7; - case '8': return 8; - case '9': return 9; - case 'a': case 'A': return 10; - case 'b': case 'B': return 11; - case 'c': case 'C': return 12; - case 'd': case 'D': return 13; - case 'e': case 'E': return 14; - case 'f': case 'F': return 15; - default: return 0; - } -} - -/* Split a line into arguments, where every argument can be in the - * following programming-language REPL-alike form: - * - * foo bar "newline are supported\n" and "\xff\x00otherstuff" - * - * The number of arguments is stored into *argc, and an array - * of sds is returned. - * - * The caller should free the resulting array of sds strings with - * sdsfreesplitres(). - * - * Note that sdscatrepr() is able to convert back a string into - * a quoted string in the same format sdssplitargs() is able to parse. - * - * The function returns the allocated tokens on success, even when the - * input string is empty, or NULL if the input contains unbalanced - * quotes or closed quotes followed by non space characters - * as in: "foo"bar or "foo' - */ -sds *sdssplitargs(const char *line, int *argc) { - const char *p = line; - char *current = NULL; - char **vector = NULL; - - *argc = 0; - while(1) { - /* skip blanks */ - while(*p && isspace(*p)) p++; - if (*p) { - /* get a token */ - int inq=0; /* set to 1 if we are in "quotes" */ - int insq=0; /* set to 1 if we are in 'single quotes' */ - int done=0; - - if (current == NULL) current = sdsempty(); - while(!done) { - if (inq) { - if (*p == '\\' && *(p+1) == 'x' && - is_hex_digit(*(p+2)) && - is_hex_digit(*(p+3))) - { - unsigned char byte; - - byte = (hex_digit_to_int(*(p+2))*16)+ - hex_digit_to_int(*(p+3)); - current = sdscatlen(current,(char*)&byte,1); - p += 3; - } else if (*p == '\\' && *(p+1)) { - char c; - - p++; - switch(*p) { - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'b': c = '\b'; break; - case 'a': c = '\a'; break; - default: c = *p; break; - } - current = sdscatlen(current,&c,1); - } else if (*p == '"') { - /* closing quote must be followed by a space or - * nothing at all. */ - if (*(p+1) && !isspace(*(p+1))) goto err; - done=1; - } else if (!*p) { - /* unterminated quotes */ - goto err; - } else { - current = sdscatlen(current,p,1); - } - } else if (insq) { - if (*p == '\\' && *(p+1) == '\'') { - p++; - current = sdscatlen(current,"'",1); - } else if (*p == '\'') { - /* closing quote must be followed by a space or - * nothing at all. */ - if (*(p+1) && !isspace(*(p+1))) goto err; - done=1; - } else if (!*p) { - /* unterminated quotes */ - goto err; - } else { - current = sdscatlen(current,p,1); - } - } else { - switch(*p) { - case ' ': - case '\n': - case '\r': - case '\t': - case '\0': - done=1; - break; - case '"': - inq=1; - break; - case '\'': - insq=1; - break; - default: - current = sdscatlen(current,p,1); - break; - } - } - if (*p) p++; - } - /* add the token to the vector */ - vector = s_realloc(vector,((*argc)+1)*sizeof(char*)); - vector[*argc] = current; - (*argc)++; - current = NULL; - } else { - /* Even on empty input string return something not NULL. */ - if (vector == NULL) vector = s_malloc(sizeof(void*)); - return vector; - } - } - -err: - while((*argc)--) - sdsfree(vector[*argc]); - s_free(vector); - if (current) sdsfree(current); - *argc = 0; - return NULL; -} - -/* Modify the string substituting all the occurrences of the set of - * characters specified in the 'from' string to the corresponding character - * in the 'to' array. - * - * For instance: sdsmapchars(mystring, "ho", "01", 2) - * will have the effect of turning the string "hello" into "0ell1". - * - * The function returns the sds string pointer, that is always the same - * as the input pointer since no resize is needed. */ -sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) { - size_t j, i, l = sdslen(s); - - for (j = 0; j < l; j++) { - for (i = 0; i < setlen; i++) { - if (s[j] == from[i]) { - s[j] = to[i]; - break; - } - } - } - return s; -} - -/* Join an array of C strings using the specified separator (also a C string). - * Returns the result as an sds string. */ -sds sdsjoin(char **argv, int argc, char *sep) { - sds join = sdsempty(); - int j; - - for (j = 0; j < argc; j++) { - join = sdscat(join, argv[j]); - if (j != argc-1) join = sdscat(join,sep); - } - return join; -} - -/* Like sdsjoin, but joins an array of SDS strings. */ -sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) { - sds join = sdsempty(); - int j; - - for (j = 0; j < argc; j++) { - join = sdscatsds(join, argv[j]); - if (j != argc-1) join = sdscatlen(join,sep,seplen); - } - return join; -} - -/* Wrappers to the allocators used by SDS. Note that SDS will actually - * just use the macros defined into sdsalloc.h in order to avoid to pay - * the overhead of function calls. Here we define these wrappers only for - * the programs SDS is linked to, if they want to touch the SDS internals - * even if they use a different allocator. */ -void *sds_malloc(size_t size) { return s_malloc(size); } -void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); } -void sds_free(void *ptr) { s_free(ptr); } - -#if defined(SDS_TEST_MAIN) -#include -#include "testhelp.h" -#include "limits.h" - -#define UNUSED(x) (void)(x) -int sdsTest(void) { - { - sds x = sdsnew("foo"), y; - - test_cond("Create a string and obtain the length", - sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) - - sdsfree(x); - x = sdsnewlen("foo",2); - test_cond("Create a string with specified length", - sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) - - x = sdscat(x,"bar"); - test_cond("Strings concatenation", - sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); - - x = sdscpy(x,"a"); - test_cond("sdscpy() against an originally longer string", - sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) - - x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); - test_cond("sdscpy() against an originally shorter string", - sdslen(x) == 33 && - memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) - - sdsfree(x); - x = sdscatprintf(sdsempty(),"%d",123); - test_cond("sdscatprintf() seems working in the base case", - sdslen(x) == 3 && memcmp(x,"123\0",4) == 0) - - sdsfree(x); - x = sdsnew("--"); - x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX); - test_cond("sdscatfmt() seems working in the base case", - sdslen(x) == 60 && - memcmp(x,"--Hello Hi! World -9223372036854775808," - "9223372036854775807--",60) == 0) - printf("[%s]\n",x); - - sdsfree(x); - x = sdsnew("--"); - x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX); - test_cond("sdscatfmt() seems working with unsigned numbers", - sdslen(x) == 35 && - memcmp(x,"--4294967295,18446744073709551615--",35) == 0) - - sdsfree(x); - x = sdsnew(" x "); - sdstrim(x," x"); - test_cond("sdstrim() works when all chars match", - sdslen(x) == 0) - - sdsfree(x); - x = sdsnew(" x "); - sdstrim(x," "); - test_cond("sdstrim() works when a single char remains", - sdslen(x) == 1 && x[0] == 'x') - - sdsfree(x); - x = sdsnew("xxciaoyyy"); - sdstrim(x,"xy"); - test_cond("sdstrim() correctly trims characters", - sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) - - y = sdsdup(x); - sdsrange(y,1,1); - test_cond("sdsrange(...,1,1)", - sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,1,-1); - test_cond("sdsrange(...,1,-1)", - sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,-2,-1); - test_cond("sdsrange(...,-2,-1)", - sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,2,1); - test_cond("sdsrange(...,2,1)", - sdslen(y) == 0 && memcmp(y,"\0",1) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,1,100); - test_cond("sdsrange(...,1,100)", - sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,100,100); - test_cond("sdsrange(...,100,100)", - sdslen(y) == 0 && memcmp(y,"\0",1) == 0) - - sdsfree(y); - sdsfree(x); - x = sdsnew("foo"); - y = sdsnew("foa"); - test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) - - sdsfree(y); - sdsfree(x); - x = sdsnew("bar"); - y = sdsnew("bar"); - test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) - - sdsfree(y); - sdsfree(x); - x = sdsnew("aar"); - y = sdsnew("bar"); - test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) - - sdsfree(y); - sdsfree(x); - x = sdsnewlen("\a\n\0foo\r",7); - y = sdscatrepr(sdsempty(),x,sdslen(x)); - test_cond("sdscatrepr(...data...)", - memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0) - - { - unsigned int oldfree; - char *p; - int step = 10, j, i; - - sdsfree(x); - sdsfree(y); - x = sdsnew("0"); - test_cond("sdsnew() free/len buffers", sdslen(x) == 1 && sdsavail(x) == 0); - - /* Run the test a few times in order to hit the first two - * SDS header types. */ - for (i = 0; i < 10; i++) { - int oldlen = sdslen(x); - x = sdsMakeRoomFor(x,step); - int type = x[-1]&SDS_TYPE_MASK; - - test_cond("sdsMakeRoomFor() len", sdslen(x) == oldlen); - if (type != SDS_TYPE_5) { - test_cond("sdsMakeRoomFor() free", sdsavail(x) >= step); - oldfree = sdsavail(x); - } - p = x+oldlen; - for (j = 0; j < step; j++) { - p[j] = 'A'+j; - } - sdsIncrLen(x,step); - } - test_cond("sdsMakeRoomFor() content", - memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ",x,101) == 0); - test_cond("sdsMakeRoomFor() final length",sdslen(x)==101); - - sdsfree(x); - } - } - test_report() - return 0; -} -#endif - -#ifdef SDS_TEST_MAIN -int main(void) { - return sdsTest(); -} -#endif diff --git a/ext/hiredis-0.14.1/sds.h b/ext/hiredis-0.14.1/sds.h deleted file mode 100644 index 13be75a9f..000000000 --- a/ext/hiredis-0.14.1/sds.h +++ /dev/null @@ -1,273 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __SDS_H -#define __SDS_H - -#define SDS_MAX_PREALLOC (1024*1024) - -#include -#include -#include - -typedef char *sds; - -/* Note: sdshdr5 is never used, we just access the flags byte directly. - * However is here to document the layout of type 5 SDS strings. */ -struct __attribute__ ((__packed__)) sdshdr5 { - unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr8 { - uint8_t len; /* used */ - uint8_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr16 { - uint16_t len; /* used */ - uint16_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr32 { - uint32_t len; /* used */ - uint32_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr64 { - uint64_t len; /* used */ - uint64_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; - -#define SDS_TYPE_5 0 -#define SDS_TYPE_8 1 -#define SDS_TYPE_16 2 -#define SDS_TYPE_32 3 -#define SDS_TYPE_64 4 -#define SDS_TYPE_MASK 7 -#define SDS_TYPE_BITS 3 -#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))); -#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) -#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) - -static inline size_t sdslen(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return SDS_TYPE_5_LEN(flags); - case SDS_TYPE_8: - return SDS_HDR(8,s)->len; - case SDS_TYPE_16: - return SDS_HDR(16,s)->len; - case SDS_TYPE_32: - return SDS_HDR(32,s)->len; - case SDS_TYPE_64: - return SDS_HDR(64,s)->len; - } - return 0; -} - -static inline size_t sdsavail(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: { - return 0; - } - case SDS_TYPE_8: { - SDS_HDR_VAR(8,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_16: { - SDS_HDR_VAR(16,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_32: { - SDS_HDR_VAR(32,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_64: { - SDS_HDR_VAR(64,s); - return sh->alloc - sh->len; - } - } - return 0; -} - -static inline void sdssetlen(sds s, size_t newlen) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - { - unsigned char *fp = ((unsigned char*)s)-1; - *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); - } - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->len = newlen; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->len = newlen; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->len = newlen; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->len = newlen; - break; - } -} - -static inline void sdsinclen(sds s, size_t inc) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - { - unsigned char *fp = ((unsigned char*)s)-1; - unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc; - *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); - } - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->len += inc; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->len += inc; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->len += inc; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->len += inc; - break; - } -} - -/* sdsalloc() = sdsavail() + sdslen() */ -static inline size_t sdsalloc(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return SDS_TYPE_5_LEN(flags); - case SDS_TYPE_8: - return SDS_HDR(8,s)->alloc; - case SDS_TYPE_16: - return SDS_HDR(16,s)->alloc; - case SDS_TYPE_32: - return SDS_HDR(32,s)->alloc; - case SDS_TYPE_64: - return SDS_HDR(64,s)->alloc; - } - return 0; -} - -static inline void sdssetalloc(sds s, size_t newlen) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - /* Nothing to do, this type has no total allocation info. */ - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->alloc = newlen; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->alloc = newlen; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->alloc = newlen; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->alloc = newlen; - break; - } -} - -sds sdsnewlen(const void *init, size_t initlen); -sds sdsnew(const char *init); -sds sdsempty(void); -sds sdsdup(const sds s); -void sdsfree(sds s); -sds sdsgrowzero(sds s, size_t len); -sds sdscatlen(sds s, const void *t, size_t len); -sds sdscat(sds s, const char *t); -sds sdscatsds(sds s, const sds t); -sds sdscpylen(sds s, const char *t, size_t len); -sds sdscpy(sds s, const char *t); - -sds sdscatvprintf(sds s, const char *fmt, va_list ap); -#ifdef __GNUC__ -sds sdscatprintf(sds s, const char *fmt, ...) - __attribute__((format(printf, 2, 3))); -#else -sds sdscatprintf(sds s, const char *fmt, ...); -#endif - -sds sdscatfmt(sds s, char const *fmt, ...); -sds sdstrim(sds s, const char *cset); -void sdsrange(sds s, int start, int end); -void sdsupdatelen(sds s); -void sdsclear(sds s); -int sdscmp(const sds s1, const sds s2); -sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); -void sdsfreesplitres(sds *tokens, int count); -void sdstolower(sds s); -void sdstoupper(sds s); -sds sdsfromlonglong(long long value); -sds sdscatrepr(sds s, const char *p, size_t len); -sds *sdssplitargs(const char *line, int *argc); -sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); -sds sdsjoin(char **argv, int argc, char *sep); -sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); - -/* Low level functions exposed to the user API */ -sds sdsMakeRoomFor(sds s, size_t addlen); -void sdsIncrLen(sds s, int incr); -sds sdsRemoveFreeSpace(sds s); -size_t sdsAllocSize(sds s); -void *sdsAllocPtr(sds s); - -/* Export the allocator used by SDS to the program using SDS. - * Sometimes the program SDS is linked to, may use a different set of - * allocators, but may want to allocate or free things that SDS will - * respectively free or allocate. */ -void *sds_malloc(size_t size); -void *sds_realloc(void *ptr, size_t size); -void sds_free(void *ptr); - -#ifdef REDIS_TEST -int sdsTest(int argc, char *argv[]); -#endif - -#endif diff --git a/ext/hiredis-0.14.1/sdsalloc.h b/ext/hiredis-0.14.1/sdsalloc.h deleted file mode 100644 index f43023c48..000000000 --- a/ext/hiredis-0.14.1/sdsalloc.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/* SDS allocator selection. - * - * This file is used in order to change the SDS allocator at compile time. - * Just define the following defines to what you want to use. Also add - * the include of your alternate allocator if needed (not needed in order - * to use the default libc allocator). */ - -#define s_malloc malloc -#define s_realloc realloc -#define s_free free diff --git a/ext/hiredis-0.14.1/test.c b/ext/hiredis-0.14.1/test.c deleted file mode 100644 index 0f5bfe572..000000000 --- a/ext/hiredis-0.14.1/test.c +++ /dev/null @@ -1,923 +0,0 @@ -#include "fmacros.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hiredis.h" -#include "net.h" - -enum connection_type { - CONN_TCP, - CONN_UNIX, - CONN_FD -}; - -struct config { - enum connection_type type; - - struct { - const char *host; - int port; - struct timeval timeout; - } tcp; - - struct { - const char *path; - } unix_sock; -}; - -/* The following lines make up our testing "framework" :) */ -static int tests = 0, fails = 0; -#define test(_s) { printf("#%02d ", ++tests); printf(_s); } -#define test_cond(_c) if(_c) printf("\033[0;32mPASSED\033[0;0m\n"); else {printf("\033[0;31mFAILED\033[0;0m\n"); fails++;} - -static long long usec(void) { - struct timeval tv; - gettimeofday(&tv,NULL); - return (((long long)tv.tv_sec)*1000000)+tv.tv_usec; -} - -/* The assert() calls below have side effects, so we need assert() - * even if we are compiling without asserts (-DNDEBUG). */ -#ifdef NDEBUG -#undef assert -#define assert(e) (void)(e) -#endif - -static redisContext *select_database(redisContext *c) { - redisReply *reply; - - /* Switch to DB 9 for testing, now that we know we can chat. */ - reply = redisCommand(c,"SELECT 9"); - assert(reply != NULL); - freeReplyObject(reply); - - /* Make sure the DB is emtpy */ - reply = redisCommand(c,"DBSIZE"); - assert(reply != NULL); - if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 0) { - /* Awesome, DB 9 is empty and we can continue. */ - freeReplyObject(reply); - } else { - printf("Database #9 is not empty, test can not continue\n"); - exit(1); - } - - return c; -} - -static int disconnect(redisContext *c, int keep_fd) { - redisReply *reply; - - /* Make sure we're on DB 9. */ - reply = redisCommand(c,"SELECT 9"); - assert(reply != NULL); - freeReplyObject(reply); - reply = redisCommand(c,"FLUSHDB"); - assert(reply != NULL); - freeReplyObject(reply); - - /* Free the context as well, but keep the fd if requested. */ - if (keep_fd) - return redisFreeKeepFd(c); - redisFree(c); - return -1; -} - -static redisContext *connect(struct config config) { - redisContext *c = NULL; - - if (config.type == CONN_TCP) { - c = redisConnect(config.tcp.host, config.tcp.port); - } else if (config.type == CONN_UNIX) { - c = redisConnectUnix(config.unix_sock.path); - } else if (config.type == CONN_FD) { - /* Create a dummy connection just to get an fd to inherit */ - redisContext *dummy_ctx = redisConnectUnix(config.unix_sock.path); - if (dummy_ctx) { - int fd = disconnect(dummy_ctx, 1); - printf("Connecting to inherited fd %d\n", fd); - c = redisConnectFd(fd); - } - } else { - assert(NULL); - } - - if (c == NULL) { - printf("Connection error: can't allocate redis context\n"); - exit(1); - } else if (c->err) { - printf("Connection error: %s\n", c->errstr); - redisFree(c); - exit(1); - } - - return select_database(c); -} - -static void test_format_commands(void) { - char *cmd; - int len; - - test("Format command without interpolation: "); - len = redisFormatCommand(&cmd,"SET foo bar"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - free(cmd); - - test("Format command with %%s string interpolation: "); - len = redisFormatCommand(&cmd,"SET %s %s","foo","bar"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - free(cmd); - - test("Format command with %%s and an empty string: "); - len = redisFormatCommand(&cmd,"SET %s %s","foo",""); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(0+2)); - free(cmd); - - test("Format command with an empty string in between proper interpolations: "); - len = redisFormatCommand(&cmd,"SET %s %s","","foo"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len) == 0 && - len == 4+4+(3+2)+4+(0+2)+4+(3+2)); - free(cmd); - - test("Format command with %%b string interpolation: "); - len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"b\0r",(size_t)3); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - free(cmd); - - test("Format command with %%b and an empty string: "); - len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"",(size_t)0); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(0+2)); - free(cmd); - - test("Format command with literal %%: "); - len = redisFormatCommand(&cmd,"SET %% %%"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 && - len == 4+4+(3+2)+4+(1+2)+4+(1+2)); - free(cmd); - - /* Vararg width depends on the type. These tests make sure that the - * width is correctly determined using the format and subsequent varargs - * can correctly be interpolated. */ -#define INTEGER_WIDTH_TEST(fmt, type) do { \ - type value = 123; \ - test("Format command with printf-delegation (" #type "): "); \ - len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \ - test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \ - len == 4+5+(12+2)+4+(9+2)); \ - free(cmd); \ -} while(0) - -#define FLOAT_WIDTH_TEST(type) do { \ - type value = 123.0; \ - test("Format command with printf-delegation (" #type "): "); \ - len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \ - test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \ - len == 4+5+(12+2)+4+(9+2)); \ - free(cmd); \ -} while(0) - - INTEGER_WIDTH_TEST("d", int); - INTEGER_WIDTH_TEST("hhd", char); - INTEGER_WIDTH_TEST("hd", short); - INTEGER_WIDTH_TEST("ld", long); - INTEGER_WIDTH_TEST("lld", long long); - INTEGER_WIDTH_TEST("u", unsigned int); - INTEGER_WIDTH_TEST("hhu", unsigned char); - INTEGER_WIDTH_TEST("hu", unsigned short); - INTEGER_WIDTH_TEST("lu", unsigned long); - INTEGER_WIDTH_TEST("llu", unsigned long long); - FLOAT_WIDTH_TEST(float); - FLOAT_WIDTH_TEST(double); - - test("Format command with invalid printf format: "); - len = redisFormatCommand(&cmd,"key:%08p %b",(void*)1234,"foo",(size_t)3); - test_cond(len == -1); - - const char *argv[3]; - argv[0] = "SET"; - argv[1] = "foo\0xxx"; - argv[2] = "bar"; - size_t lens[3] = { 3, 7, 3 }; - int argc = 3; - - test("Format command by passing argc/argv without lengths: "); - len = redisFormatCommandArgv(&cmd,argc,argv,NULL); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - free(cmd); - - test("Format command by passing argc/argv with lengths: "); - len = redisFormatCommandArgv(&cmd,argc,argv,lens); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(7+2)+4+(3+2)); - free(cmd); - - sds sds_cmd; - - sds_cmd = sdsempty(); - test("Format command into sds by passing argc/argv without lengths: "); - len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,NULL); - test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - sdsfree(sds_cmd); - - sds_cmd = sdsempty(); - test("Format command into sds by passing argc/argv with lengths: "); - len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,lens); - test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(7+2)+4+(3+2)); - sdsfree(sds_cmd); -} - -static void test_append_formatted_commands(struct config config) { - redisContext *c; - redisReply *reply; - char *cmd; - int len; - - c = connect(config); - - test("Append format command: "); - - len = redisFormatCommand(&cmd, "SET foo bar"); - - test_cond(redisAppendFormattedCommand(c, cmd, len) == REDIS_OK); - - assert(redisGetReply(c, (void*)&reply) == REDIS_OK); - - free(cmd); - freeReplyObject(reply); - - disconnect(c, 0); -} - -static void test_reply_reader(void) { - redisReader *reader; - void *reply; - int ret; - int i; - - test("Error handling in reply parser: "); - reader = redisReaderCreate(); - redisReaderFeed(reader,(char*)"@foo\r\n",6); - ret = redisReaderGetReply(reader,NULL); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0); - redisReaderFree(reader); - - /* when the reply already contains multiple items, they must be free'd - * on an error. valgrind will bark when this doesn't happen. */ - test("Memory cleanup in reply parser: "); - reader = redisReaderCreate(); - redisReaderFeed(reader,(char*)"*2\r\n",4); - redisReaderFeed(reader,(char*)"$5\r\nhello\r\n",11); - redisReaderFeed(reader,(char*)"@foo\r\n",6); - ret = redisReaderGetReply(reader,NULL); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0); - redisReaderFree(reader); - - test("Set error on nested multi bulks with depth > 7: "); - reader = redisReaderCreate(); - - for (i = 0; i < 9; i++) { - redisReaderFeed(reader,(char*)"*1\r\n",4); - } - - ret = redisReaderGetReply(reader,NULL); - test_cond(ret == REDIS_ERR && - strncasecmp(reader->errstr,"No support for",14) == 0); - redisReaderFree(reader); - - test("Correctly parses LLONG_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":9223372036854775807\r\n",22); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_INTEGER && - ((redisReply*)reply)->integer == LLONG_MAX); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when > LLONG_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":9223372036854775808\r\n",22); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bad integer value") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Correctly parses LLONG_MIN: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":-9223372036854775808\r\n",23); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_INTEGER && - ((redisReply*)reply)->integer == LLONG_MIN); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when < LLONG_MIN: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":-9223372036854775809\r\n",23); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bad integer value") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when array < -1: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "*-2\r\n+asdf\r\n",12); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when bulk < -1: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "$-2\r\nasdf\r\n",11); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bulk string length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when array > INT_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "*9223372036854775807\r\n+asdf\r\n",29); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - -#if LLONG_MAX > SIZE_MAX - test("Set error when bulk > SIZE_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "$9223372036854775807\r\nasdf\r\n",28); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bulk string length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); -#endif - - test("Works with NULL functions for reply: "); - reader = redisReaderCreate(); - reader->fn = NULL; - redisReaderFeed(reader,(char*)"+OK\r\n",5); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS); - redisReaderFree(reader); - - test("Works when a single newline (\\r\\n) covers two calls to feed: "); - reader = redisReaderCreate(); - reader->fn = NULL; - redisReaderFeed(reader,(char*)"+OK\r",4); - ret = redisReaderGetReply(reader,&reply); - assert(ret == REDIS_OK && reply == NULL); - redisReaderFeed(reader,(char*)"\n",1); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS); - redisReaderFree(reader); - - test("Don't reset state after protocol error: "); - reader = redisReaderCreate(); - reader->fn = NULL; - redisReaderFeed(reader,(char*)"x",1); - ret = redisReaderGetReply(reader,&reply); - assert(ret == REDIS_ERR); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && reply == NULL); - redisReaderFree(reader); - - /* Regression test for issue #45 on GitHub. */ - test("Don't do empty allocation for empty multi bulk: "); - reader = redisReaderCreate(); - redisReaderFeed(reader,(char*)"*0\r\n",4); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_ARRAY && - ((redisReply*)reply)->elements == 0); - freeReplyObject(reply); - redisReaderFree(reader); -} - -static void test_free_null(void) { - void *redisCtx = NULL; - void *reply = NULL; - - test("Don't fail when redisFree is passed a NULL value: "); - redisFree(redisCtx); - test_cond(redisCtx == NULL); - - test("Don't fail when freeReplyObject is passed a NULL value: "); - freeReplyObject(reply); - test_cond(reply == NULL); -} - -static void test_blocking_connection_errors(void) { - redisContext *c; - - test("Returns error when host cannot be resolved: "); - c = redisConnect((char*)"idontexist.test", 6379); - test_cond(c->err == REDIS_ERR_OTHER && - (strcmp(c->errstr,"Name or service not known") == 0 || - strcmp(c->errstr,"Can't resolve: idontexist.test") == 0 || - strcmp(c->errstr,"nodename nor servname provided, or not known") == 0 || - strcmp(c->errstr,"No address associated with hostname") == 0 || - strcmp(c->errstr,"Temporary failure in name resolution") == 0 || - strcmp(c->errstr,"hostname nor servname provided, or not known") == 0 || - strcmp(c->errstr,"no address associated with name") == 0)); - redisFree(c); - - test("Returns error when the port is not open: "); - c = redisConnect((char*)"localhost", 1); - test_cond(c->err == REDIS_ERR_IO && - strcmp(c->errstr,"Connection refused") == 0); - redisFree(c); - - test("Returns error when the unix_sock socket path doesn't accept connections: "); - c = redisConnectUnix((char*)"/tmp/idontexist.sock"); - test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */ - redisFree(c); -} - -static void test_blocking_connection(struct config config) { - redisContext *c; - redisReply *reply; - - c = connect(config); - - test("Is able to deliver commands: "); - reply = redisCommand(c,"PING"); - test_cond(reply->type == REDIS_REPLY_STATUS && - strcasecmp(reply->str,"pong") == 0) - freeReplyObject(reply); - - test("Is a able to send commands verbatim: "); - reply = redisCommand(c,"SET foo bar"); - test_cond (reply->type == REDIS_REPLY_STATUS && - strcasecmp(reply->str,"ok") == 0) - freeReplyObject(reply); - - test("%%s String interpolation works: "); - reply = redisCommand(c,"SET %s %s","foo","hello world"); - freeReplyObject(reply); - reply = redisCommand(c,"GET foo"); - test_cond(reply->type == REDIS_REPLY_STRING && - strcmp(reply->str,"hello world") == 0); - freeReplyObject(reply); - - test("%%b String interpolation works: "); - reply = redisCommand(c,"SET %b %b","foo",(size_t)3,"hello\x00world",(size_t)11); - freeReplyObject(reply); - reply = redisCommand(c,"GET foo"); - test_cond(reply->type == REDIS_REPLY_STRING && - memcmp(reply->str,"hello\x00world",11) == 0) - - test("Binary reply length is correct: "); - test_cond(reply->len == 11) - freeReplyObject(reply); - - test("Can parse nil replies: "); - reply = redisCommand(c,"GET nokey"); - test_cond(reply->type == REDIS_REPLY_NIL) - freeReplyObject(reply); - - /* test 7 */ - test("Can parse integer replies: "); - reply = redisCommand(c,"INCR mycounter"); - test_cond(reply->type == REDIS_REPLY_INTEGER && reply->integer == 1) - freeReplyObject(reply); - - test("Can parse multi bulk replies: "); - freeReplyObject(redisCommand(c,"LPUSH mylist foo")); - freeReplyObject(redisCommand(c,"LPUSH mylist bar")); - reply = redisCommand(c,"LRANGE mylist 0 -1"); - test_cond(reply->type == REDIS_REPLY_ARRAY && - reply->elements == 2 && - !memcmp(reply->element[0]->str,"bar",3) && - !memcmp(reply->element[1]->str,"foo",3)) - freeReplyObject(reply); - - /* m/e with multi bulk reply *before* other reply. - * specifically test ordering of reply items to parse. */ - test("Can handle nested multi bulk replies: "); - freeReplyObject(redisCommand(c,"MULTI")); - freeReplyObject(redisCommand(c,"LRANGE mylist 0 -1")); - freeReplyObject(redisCommand(c,"PING")); - reply = (redisCommand(c,"EXEC")); - test_cond(reply->type == REDIS_REPLY_ARRAY && - reply->elements == 2 && - reply->element[0]->type == REDIS_REPLY_ARRAY && - reply->element[0]->elements == 2 && - !memcmp(reply->element[0]->element[0]->str,"bar",3) && - !memcmp(reply->element[0]->element[1]->str,"foo",3) && - reply->element[1]->type == REDIS_REPLY_STATUS && - strcasecmp(reply->element[1]->str,"pong") == 0); - freeReplyObject(reply); - - disconnect(c, 0); -} - -static void test_blocking_connection_timeouts(struct config config) { - redisContext *c; - redisReply *reply; - ssize_t s; - const char *cmd = "DEBUG SLEEP 3\r\n"; - struct timeval tv; - - c = connect(config); - test("Successfully completes a command when the timeout is not exceeded: "); - reply = redisCommand(c,"SET foo fast"); - freeReplyObject(reply); - tv.tv_sec = 0; - tv.tv_usec = 10000; - redisSetTimeout(c, tv); - reply = redisCommand(c, "GET foo"); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STRING && memcmp(reply->str, "fast", 4) == 0); - freeReplyObject(reply); - disconnect(c, 0); - - c = connect(config); - test("Does not return a reply when the command times out: "); - s = write(c->fd, cmd, strlen(cmd)); - tv.tv_sec = 0; - tv.tv_usec = 10000; - redisSetTimeout(c, tv); - reply = redisCommand(c, "GET foo"); - test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO && strcmp(c->errstr, "Resource temporarily unavailable") == 0); - freeReplyObject(reply); - - test("Reconnect properly reconnects after a timeout: "); - redisReconnect(c); - reply = redisCommand(c, "PING"); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0); - freeReplyObject(reply); - - test("Reconnect properly uses owned parameters: "); - config.tcp.host = "foo"; - config.unix_sock.path = "foo"; - redisReconnect(c); - reply = redisCommand(c, "PING"); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0); - freeReplyObject(reply); - - disconnect(c, 0); -} - -static void test_blocking_io_errors(struct config config) { - redisContext *c; - redisReply *reply; - void *_reply; - int major, minor; - - /* Connect to target given by config. */ - c = connect(config); - { - /* Find out Redis version to determine the path for the next test */ - const char *field = "redis_version:"; - char *p, *eptr; - - reply = redisCommand(c,"INFO"); - p = strstr(reply->str,field); - major = strtol(p+strlen(field),&eptr,10); - p = eptr+1; /* char next to the first "." */ - minor = strtol(p,&eptr,10); - freeReplyObject(reply); - } - - test("Returns I/O error when the connection is lost: "); - reply = redisCommand(c,"QUIT"); - if (major > 2 || (major == 2 && minor > 0)) { - /* > 2.0 returns OK on QUIT and read() should be issued once more - * to know the descriptor is at EOF. */ - test_cond(strcasecmp(reply->str,"OK") == 0 && - redisGetReply(c,&_reply) == REDIS_ERR); - freeReplyObject(reply); - } else { - test_cond(reply == NULL); - } - - /* On 2.0, QUIT will cause the connection to be closed immediately and - * the read(2) for the reply on QUIT will set the error to EOF. - * On >2.0, QUIT will return with OK and another read(2) needed to be - * issued to find out the socket was closed by the server. In both - * conditions, the error will be set to EOF. */ - assert(c->err == REDIS_ERR_EOF && - strcmp(c->errstr,"Server closed the connection") == 0); - redisFree(c); - - c = connect(config); - test("Returns I/O error on socket timeout: "); - struct timeval tv = { 0, 1000 }; - assert(redisSetTimeout(c,tv) == REDIS_OK); - test_cond(redisGetReply(c,&_reply) == REDIS_ERR && - c->err == REDIS_ERR_IO && errno == EAGAIN); - redisFree(c); -} - -static void test_invalid_timeout_errors(struct config config) { - redisContext *c; - - test("Set error when an invalid timeout usec value is given to redisConnectWithTimeout: "); - - config.tcp.timeout.tv_sec = 0; - config.tcp.timeout.tv_usec = 10000001; - - c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout); - - test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); - redisFree(c); - - test("Set error when an invalid timeout sec value is given to redisConnectWithTimeout: "); - - config.tcp.timeout.tv_sec = (((LONG_MAX) - 999) / 1000) + 1; - config.tcp.timeout.tv_usec = 0; - - c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout); - - test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); - redisFree(c); -} - -static void test_throughput(struct config config) { - redisContext *c = connect(config); - redisReply **replies; - int i, num; - long long t1, t2; - - test("Throughput:\n"); - for (i = 0; i < 500; i++) - freeReplyObject(redisCommand(c,"LPUSH mylist foo")); - - num = 1000; - replies = malloc(sizeof(redisReply*)*num); - t1 = usec(); - for (i = 0; i < num; i++) { - replies[i] = redisCommand(c,"PING"); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = malloc(sizeof(redisReply*)*num); - t1 = usec(); - for (i = 0; i < num; i++) { - replies[i] = redisCommand(c,"LRANGE mylist 0 499"); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY); - assert(replies[i] != NULL && replies[i]->elements == 500); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = malloc(sizeof(redisReply*)*num); - t1 = usec(); - for (i = 0; i < num; i++) { - replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0); - - num = 10000; - replies = malloc(sizeof(redisReply*)*num); - for (i = 0; i < num; i++) - redisAppendCommand(c,"PING"); - t1 = usec(); - for (i = 0; i < num; i++) { - assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = malloc(sizeof(redisReply*)*num); - for (i = 0; i < num; i++) - redisAppendCommand(c,"LRANGE mylist 0 499"); - t1 = usec(); - for (i = 0; i < num; i++) { - assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY); - assert(replies[i] != NULL && replies[i]->elements == 500); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = malloc(sizeof(redisReply*)*num); - for (i = 0; i < num; i++) - redisAppendCommand(c,"INCRBY incrkey %d", 1000000); - t1 = usec(); - for (i = 0; i < num; i++) { - assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); - - disconnect(c, 0); -} - -// static long __test_callback_flags = 0; -// static void __test_callback(redisContext *c, void *privdata) { -// ((void)c); -// /* Shift to detect execution order */ -// __test_callback_flags <<= 8; -// __test_callback_flags |= (long)privdata; -// } -// -// static void __test_reply_callback(redisContext *c, redisReply *reply, void *privdata) { -// ((void)c); -// /* Shift to detect execution order */ -// __test_callback_flags <<= 8; -// __test_callback_flags |= (long)privdata; -// if (reply) freeReplyObject(reply); -// } -// -// static redisContext *__connect_nonblock() { -// /* Reset callback flags */ -// __test_callback_flags = 0; -// return redisConnectNonBlock("127.0.0.1", port, NULL); -// } -// -// static void test_nonblocking_connection() { -// redisContext *c; -// int wdone = 0; -// -// test("Calls command callback when command is issued: "); -// c = __connect_nonblock(); -// redisSetCommandCallback(c,__test_callback,(void*)1); -// redisCommand(c,"PING"); -// test_cond(__test_callback_flags == 1); -// redisFree(c); -// -// test("Calls disconnect callback on redisDisconnect: "); -// c = __connect_nonblock(); -// redisSetDisconnectCallback(c,__test_callback,(void*)2); -// redisDisconnect(c); -// test_cond(__test_callback_flags == 2); -// redisFree(c); -// -// test("Calls disconnect callback and free callback on redisFree: "); -// c = __connect_nonblock(); -// redisSetDisconnectCallback(c,__test_callback,(void*)2); -// redisSetFreeCallback(c,__test_callback,(void*)4); -// redisFree(c); -// test_cond(__test_callback_flags == ((2 << 8) | 4)); -// -// test("redisBufferWrite against empty write buffer: "); -// c = __connect_nonblock(); -// test_cond(redisBufferWrite(c,&wdone) == REDIS_OK && wdone == 1); -// redisFree(c); -// -// test("redisBufferWrite against not yet connected fd: "); -// c = __connect_nonblock(); -// redisCommand(c,"PING"); -// test_cond(redisBufferWrite(c,NULL) == REDIS_ERR && -// strncmp(c->error,"write:",6) == 0); -// redisFree(c); -// -// test("redisBufferWrite against closed fd: "); -// c = __connect_nonblock(); -// redisCommand(c,"PING"); -// redisDisconnect(c); -// test_cond(redisBufferWrite(c,NULL) == REDIS_ERR && -// strncmp(c->error,"write:",6) == 0); -// redisFree(c); -// -// test("Process callbacks in the right sequence: "); -// c = __connect_nonblock(); -// redisCommandWithCallback(c,__test_reply_callback,(void*)1,"PING"); -// redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING"); -// redisCommandWithCallback(c,__test_reply_callback,(void*)3,"PING"); -// -// /* Write output buffer */ -// wdone = 0; -// while(!wdone) { -// usleep(500); -// redisBufferWrite(c,&wdone); -// } -// -// /* Read until at least one callback is executed (the 3 replies will -// * arrive in a single packet, causing all callbacks to be executed in -// * a single pass). */ -// while(__test_callback_flags == 0) { -// assert(redisBufferRead(c) == REDIS_OK); -// redisProcessCallbacks(c); -// } -// test_cond(__test_callback_flags == 0x010203); -// redisFree(c); -// -// test("redisDisconnect executes pending callbacks with NULL reply: "); -// c = __connect_nonblock(); -// redisSetDisconnectCallback(c,__test_callback,(void*)1); -// redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING"); -// redisDisconnect(c); -// test_cond(__test_callback_flags == 0x0201); -// redisFree(c); -// } - -int main(int argc, char **argv) { - struct config cfg = { - .tcp = { - .host = "127.0.0.1", - .port = 6379 - }, - .unix_sock = { - .path = "/tmp/redis.sock" - } - }; - int throughput = 1; - int test_inherit_fd = 1; - - /* Ignore broken pipe signal (for I/O error tests). */ - signal(SIGPIPE, SIG_IGN); - - /* Parse command line options. */ - argv++; argc--; - while (argc) { - if (argc >= 2 && !strcmp(argv[0],"-h")) { - argv++; argc--; - cfg.tcp.host = argv[0]; - } else if (argc >= 2 && !strcmp(argv[0],"-p")) { - argv++; argc--; - cfg.tcp.port = atoi(argv[0]); - } else if (argc >= 2 && !strcmp(argv[0],"-s")) { - argv++; argc--; - cfg.unix_sock.path = argv[0]; - } else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) { - throughput = 0; - } else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) { - test_inherit_fd = 0; - } else { - fprintf(stderr, "Invalid argument: %s\n", argv[0]); - exit(1); - } - argv++; argc--; - } - - test_format_commands(); - test_reply_reader(); - test_blocking_connection_errors(); - test_free_null(); - - printf("\nTesting against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port); - cfg.type = CONN_TCP; - test_blocking_connection(cfg); - test_blocking_connection_timeouts(cfg); - test_blocking_io_errors(cfg); - test_invalid_timeout_errors(cfg); - test_append_formatted_commands(cfg); - if (throughput) test_throughput(cfg); - - printf("\nTesting against Unix socket connection (%s):\n", cfg.unix_sock.path); - cfg.type = CONN_UNIX; - test_blocking_connection(cfg); - test_blocking_connection_timeouts(cfg); - test_blocking_io_errors(cfg); - if (throughput) test_throughput(cfg); - - if (test_inherit_fd) { - printf("\nTesting against inherited fd (%s):\n", cfg.unix_sock.path); - cfg.type = CONN_FD; - test_blocking_connection(cfg); - } - - - if (fails) { - printf("*** %d TESTS FAILED ***\n", fails); - return 1; - } - - printf("ALL TESTS PASSED\n"); - return 0; -} diff --git a/ext/hiredis-0.14.1/win32.h b/ext/hiredis-0.14.1/win32.h deleted file mode 100644 index 1a27c18f2..000000000 --- a/ext/hiredis-0.14.1/win32.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _WIN32_HELPER_INCLUDE -#define _WIN32_HELPER_INCLUDE -#ifdef _MSC_VER - -#ifndef inline -#define inline __inline -#endif - -#ifndef va_copy -#define va_copy(d,s) ((d) = (s)) -#endif - -#ifndef snprintf -#define snprintf c99_snprintf - -__inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) -{ - int count = -1; - - if (size != 0) - count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); - if (count == -1) - count = _vscprintf(format, ap); - - return count; -} - -__inline int c99_snprintf(char* str, size_t size, const char* format, ...) -{ - int count; - va_list ap; - - va_start(ap, format); - count = c99_vsnprintf(str, size, format, ap); - va_end(ap); - - return count; -} -#endif - -#endif -#endif \ No newline at end of file diff --git a/ext/hiredis-1.0.2/.gitignore b/ext/hiredis-1.0.2/.gitignore deleted file mode 100644 index 056959ffe..000000000 --- a/ext/hiredis-1.0.2/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -/hiredis-test -/examples/hiredis-example* -/*.o -/*.so -/*.dylib -/*.a -/*.pc -*.dSYM -tags diff --git a/ext/hiredis-1.0.2/.travis.yml b/ext/hiredis-1.0.2/.travis.yml deleted file mode 100644 index f9a9460ff..000000000 --- a/ext/hiredis-1.0.2/.travis.yml +++ /dev/null @@ -1,131 +0,0 @@ -language: c -compiler: - - gcc - - clang - -os: - - linux - - osx - -dist: bionic - -branches: - only: - - staging - - trying - - master - - /^release\/.*$/ - -install: - - if [ "$BITS" == "64" ]; then - wget https://github.com/redis/redis/archive/6.0.6.tar.gz; - tar -xzvf 6.0.6.tar.gz; - pushd redis-6.0.6 && BUILD_TLS=yes make && export PATH=$PWD/src:$PATH && popd; - fi - -before_script: - - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - curl -O https://distfiles.macports.org/MacPorts/MacPorts-2.6.2-10.13-HighSierra.pkg; - sudo installer -pkg MacPorts-2.6.2-10.13-HighSierra.pkg -target /; - export PATH=$PATH:/opt/local/bin && sudo port -v selfupdate; - sudo port -N install openssl redis; - fi; - -addons: - apt: - sources: - - sourceline: 'ppa:chris-lea/redis-server' - packages: - - libc6-dbg - - libc6-dev - - libc6:i386 - - libc6-dev-i386 - - libc6-dbg:i386 - - gcc-multilib - - g++-multilib - - libssl-dev - - libssl-dev:i386 - - valgrind - - redis - -env: - - BITS="32" - - BITS="64" - -script: - - EXTRA_CMAKE_OPTS="-DENABLE_EXAMPLES:BOOL=ON -DENABLE_SSL:BOOL=ON"; - if [ "$BITS" == "64" ]; then - EXTRA_CMAKE_OPTS="$EXTRA_CMAKE_OPTS -DENABLE_SSL_TESTS:BOOL=ON"; - fi; - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - if [ "$BITS" == "32" ]; then - CFLAGS="-m32 -Werror"; - CXXFLAGS="-m32 -Werror"; - LDFLAGS="-m32"; - EXTRA_CMAKE_OPTS=; - else - CFLAGS="-Werror"; - CXXFLAGS="-Werror"; - fi; - else - TEST_PREFIX="valgrind --track-origins=yes --leak-check=full"; - if [ "$BITS" == "32" ]; then - CFLAGS="-m32 -Werror"; - CXXFLAGS="-m32 -Werror"; - LDFLAGS="-m32"; - EXTRA_CMAKE_OPTS=; - else - CFLAGS="-Werror"; - CXXFLAGS="-Werror"; - fi; - fi; - export CFLAGS CXXFLAGS LDFLAGS TEST_PREFIX EXTRA_CMAKE_OPTS - - make && make clean; - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - if [ "$BITS" == "64" ]; then - OPENSSL_PREFIX="$(ls -d /usr/local/Cellar/openssl@1.1/*)" USE_SSL=1 make; - fi; - else - USE_SSL=1 make; - fi; - - mkdir build/ && cd build/ - - cmake .. ${EXTRA_CMAKE_OPTS} - - make VERBOSE=1 - - if [ "$BITS" == "64" ]; then - TEST_SSL=1 SKIPS_AS_FAILS=1 ctest -V; - else - SKIPS_AS_FAILS=1 ctest -V; - fi; - -jobs: - include: - # Windows MinGW cross compile on Linux - - os: linux - dist: xenial - compiler: mingw - addons: - apt: - packages: - - ninja-build - - gcc-mingw-w64-x86-64 - - g++-mingw-w64-x86-64 - script: - - mkdir build && cd build - - CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_BUILD_WITH_INSTALL_RPATH=on - - ninja -v - - # Windows MSVC 2017 - - os: windows - compiler: msvc - env: - - MATRIX_EVAL="CC=cl.exe && CXX=cl.exe" - before_install: - - eval "${MATRIX_EVAL}" - install: - - choco install ninja - - choco install -y memurai-developer - script: - - mkdir build && cd build - - cmd.exe //C 'C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat' amd64 '&&' - cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DENABLE_EXAMPLES=ON '&&' ninja -v - - ./hiredis-test.exe diff --git a/ext/hiredis-1.0.2/CHANGELOG.md b/ext/hiredis-1.0.2/CHANGELOG.md deleted file mode 100644 index 2a2bc314a..000000000 --- a/ext/hiredis-1.0.2/CHANGELOG.md +++ /dev/null @@ -1,364 +0,0 @@ -## [1.0.2](https://github.com/redis/hiredis/tree/v1.0.2) - (2021-10-07) - -Announcing Hiredis v1.0.2, which fixes CVE-2021-32765 but returns the SONAME to the correct value of `1.0.0`. - -- [Revert SONAME bump](https://github.com/redis/hiredis/commit/d4e6f109a064690cde64765c654e679fea1d3548) - ([Michael Grunder](https://github.com/michael-grunder)) - -## [1.0.1](https://github.com/redis/hiredis/tree/v1.0.1) - (2021-10-04) - -This release erroneously bumped the SONAME, please use [1.0.2](https://github.com/redis/hiredis/tree/v1.0.2) - -Announcing Hiredis v1.0.1, a security release fixing CVE-2021-32765 - -- Fix for [CVE-2021-32765](https://github.com/redis/hiredis/security/advisories/GHSA-hfm9-39pp-55p2) - [commit](https://github.com/redis/hiredis/commit/76a7b10005c70babee357a7d0f2becf28ec7ed1e) - ([Yossi Gottlieb](https://github.com/yossigo)) - -_Thanks to [Yossi Gottlieb](https://github.com/yossigo) for the security fix and to [Microsoft Security Vulnerability Research](https://www.microsoft.com/en-us/msrc/msvr) for finding the bug._ :sparkling_heart: - -## [1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) - (2020-08-03) - -Announcing Hiredis v1.0.0, which adds support for RESP3, SSL connections, allocator injection, and better Windows support! :tada: - -_A big thanks to everyone who helped with this release. The following list includes everyone who contributed at least five lines, sorted by lines contributed._ :sparkling_heart: - -[Michael Grunder](https://github.com/michael-grunder), [Yossi Gottlieb](https://github.com/yossigo), -[Mark Nunberg](https://github.com/mnunberg), [Marcus Geelnard](https://github.com/mbitsnbites), -[Justin Brewer](https://github.com/justinbrewer), [Valentino Geron](https://github.com/valentinogeron), -[Minun Dragonation](https://github.com/dragonation), [Omri Steiner](https://github.com/OmriSteiner), -[Sangmoon Yi](https://github.com/jman-krafton), [Jinjiazh](https://github.com/jinjiazhang), -[Odin Hultgren Van Der Horst](https://github.com/Miniwoffer), [Muhammad Zahalqa](https://github.com/tryfinally), -[Nick Rivera](https://github.com/heronr), [Qi Yang](https://github.com/movebean), -[kevin1018](https://github.com/kevin1018) - -[Full Changelog](https://github.com/redis/hiredis/compare/v0.14.1...v1.0.0) - -**BREAKING CHANGES**: - -* `redisOptions` now has two timeout fields. One for connecting, and one for commands. If you're presently using `options->timeout` you will need to change it to use `options->connect_timeout`. (See [example](https://github.com/redis/hiredis/commit/38b5ae543f5c99eb4ccabbe277770fc6bc81226f#diff-86ba39d37aa829c8c82624cce4f049fbL36)) - -* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now protocol errors. This is consistent - with the RESP specification. On 32-bit platforms, the upper bound is lowered to `SIZE_MAX`. - -* `redisReplyObjectFunctions.createArray` now takes `size_t` for its length parameter. - -**New features:** -- Support for RESP3 - [\#697](https://github.com/redis/hiredis/pull/697), - [\#805](https://github.com/redis/hiredis/pull/805), - [\#819](https://github.com/redis/hiredis/pull/819), - [\#841](https://github.com/redis/hiredis/pull/841) - ([Yossi Gottlieb](https://github.com/yossigo), [Michael Grunder](https://github.com/michael-grunder)) -- Support for SSL connections - [\#645](https://github.com/redis/hiredis/pull/645), - [\#699](https://github.com/redis/hiredis/pull/699), - [\#702](https://github.com/redis/hiredis/pull/702), - [\#708](https://github.com/redis/hiredis/pull/708), - [\#711](https://github.com/redis/hiredis/pull/711), - [\#821](https://github.com/redis/hiredis/pull/821), - [more](https://github.com/redis/hiredis/pulls?q=is%3Apr+is%3Amerged+SSL) - ([Mark Nunberg](https://github.com/mnunberg), [Yossi Gottlieb](https://github.com/yossigo)) -- Run-time allocator injection - [\#800](https://github.com/redis/hiredis/pull/800) - ([Michael Grunder](https://github.com/michael-grunder)) -- Improved Windows support (including MinGW and Windows CI) - [\#652](https://github.com/redis/hiredis/pull/652), - [\#663](https://github.com/redis/hiredis/pull/663) - ([Marcus Geelnard](https://www.bitsnbites.eu/author/m/)) -- Adds support for distinct connect and command timeouts - [\#839](https://github.com/redis/hiredis/pull/839), - [\#829](https://github.com/redis/hiredis/pull/829) - ([Valentino Geron](https://github.com/valentinogeron)) -- Add generic pointer and destructor to `redisContext` that users can use for context. - [\#855](https://github.com/redis/hiredis/pull/855) - ([Michael Grunder](https://github.com/michael-grunder)) - -**Closed issues (that involved code changes):** - -- Makefile does not install TLS libraries [\#809](https://github.com/redis/hiredis/issues/809) -- redisConnectWithOptions should not set command timeout [\#722](https://github.com/redis/hiredis/issues/722), [\#829](https://github.com/redis/hiredis/pull/829) ([valentinogeron](https://github.com/valentinogeron)) -- Fix integer overflow in `sdsrange` [\#827](https://github.com/redis/hiredis/issues/827) -- INFO & CLUSTER commands failed when using RESP3 [\#802](https://github.com/redis/hiredis/issues/802) -- Windows compatibility patches [\#687](https://github.com/redis/hiredis/issues/687), [\#838](https://github.com/redis/hiredis/issues/838), [\#842](https://github.com/redis/hiredis/issues/842) -- RESP3 PUSH messages incorrectly use pending callback [\#825](https://github.com/redis/hiredis/issues/825) -- Asynchronous PSUBSCRIBE command fails when using RESP3 [\#815](https://github.com/redis/hiredis/issues/815) -- New SSL API [\#804](https://github.com/redis/hiredis/issues/804), [\#813](https://github.com/redis/hiredis/issues/813) -- Hard-coded limit of nested reply depth [\#794](https://github.com/redis/hiredis/issues/794) -- Fix TCP_NODELAY in Windows/OSX [\#679](https://github.com/redis/hiredis/issues/679), [\#690](https://github.com/redis/hiredis/issues/690), [\#779](https://github.com/redis/hiredis/issues/779), [\#785](https://github.com/redis/hiredis/issues/785), -- Added timers to libev adapter. [\#778](https://github.com/redis/hiredis/issues/778), [\#795](https://github.com/redis/hiredis/pull/795) -- Initialization discards const qualifier [\#777](https://github.com/redis/hiredis/issues/777) -- \[BUG\]\[MinGW64\] Error setting socket timeout [\#775](https://github.com/redis/hiredis/issues/775) -- undefined reference to hi_malloc [\#769](https://github.com/redis/hiredis/issues/769) -- hiredis pkg-config file incorrectly ignores multiarch libdir spec'n [\#767](https://github.com/redis/hiredis/issues/767) -- Don't use -G to build shared object on Solaris [\#757](https://github.com/redis/hiredis/issues/757) -- error when make USE\_SSL=1 [\#748](https://github.com/redis/hiredis/issues/748) -- Allow to change SSL Mode [\#646](https://github.com/redis/hiredis/issues/646) -- hiredis/adapters/libevent.h memleak [\#618](https://github.com/redis/hiredis/issues/618) -- redisLibuvPoll crash when server closes the connetion [\#545](https://github.com/redis/hiredis/issues/545) -- about redisAsyncDisconnect question [\#518](https://github.com/redis/hiredis/issues/518) -- hiredis adapters libuv error for help [\#508](https://github.com/redis/hiredis/issues/508) -- API/ABI changes analysis [\#506](https://github.com/redis/hiredis/issues/506) -- Memory leak patch in Redis [\#502](https://github.com/redis/hiredis/issues/502) -- Remove the depth limitation [\#421](https://github.com/redis/hiredis/issues/421) - -**Merged pull requests:** - -- Move SSL management to a distinct private pointer [\#855](https://github.com/redis/hiredis/pull/855) ([michael-grunder](https://github.com/michael-grunder)) -- Move include to sockcompat.h to maintain style [\#850](https://github.com/redis/hiredis/pull/850) ([michael-grunder](https://github.com/michael-grunder)) -- Remove erroneous tag and add license to push example [\#849](https://github.com/redis/hiredis/pull/849) ([michael-grunder](https://github.com/michael-grunder)) -- fix windows compiling with mingw [\#848](https://github.com/redis/hiredis/pull/848) ([rmalizia44](https://github.com/rmalizia44)) -- Some Windows quality of life improvements. [\#846](https://github.com/redis/hiredis/pull/846) ([michael-grunder](https://github.com/michael-grunder)) -- Use \_WIN32 define instead of WIN32 [\#845](https://github.com/redis/hiredis/pull/845) ([michael-grunder](https://github.com/michael-grunder)) -- Non Linux CI fixes [\#844](https://github.com/redis/hiredis/pull/844) ([michael-grunder](https://github.com/michael-grunder)) -- Resp3 oob push support [\#841](https://github.com/redis/hiredis/pull/841) ([michael-grunder](https://github.com/michael-grunder)) -- fix \#785: defer TCP\_NODELAY in async tcp connections [\#836](https://github.com/redis/hiredis/pull/836) ([OmriSteiner](https://github.com/OmriSteiner)) -- sdsrange overflow fix [\#830](https://github.com/redis/hiredis/pull/830) ([michael-grunder](https://github.com/michael-grunder)) -- Use explicit pointer casting for c++ compatibility [\#826](https://github.com/redis/hiredis/pull/826) ([aureus1](https://github.com/aureus1)) -- Document allocator injection and completeness fix in test.c [\#824](https://github.com/redis/hiredis/pull/824) ([michael-grunder](https://github.com/michael-grunder)) -- Use unique names for allocator struct members [\#823](https://github.com/redis/hiredis/pull/823) ([michael-grunder](https://github.com/michael-grunder)) -- New SSL API to replace redisSecureConnection\(\). [\#821](https://github.com/redis/hiredis/pull/821) ([yossigo](https://github.com/yossigo)) -- Add logic to handle RESP3 push messages [\#819](https://github.com/redis/hiredis/pull/819) ([michael-grunder](https://github.com/michael-grunder)) -- Use standrad isxdigit instead of custom helper function. [\#814](https://github.com/redis/hiredis/pull/814) ([tryfinally](https://github.com/tryfinally)) -- Fix missing SSL build/install options. [\#812](https://github.com/redis/hiredis/pull/812) ([yossigo](https://github.com/yossigo)) -- Add link to ABI tracker [\#808](https://github.com/redis/hiredis/pull/808) ([michael-grunder](https://github.com/michael-grunder)) -- Resp3 verbatim string support [\#805](https://github.com/redis/hiredis/pull/805) ([michael-grunder](https://github.com/michael-grunder)) -- Allow users to replace allocator and handle OOM everywhere. [\#800](https://github.com/redis/hiredis/pull/800) ([michael-grunder](https://github.com/michael-grunder)) -- Remove nested depth limitation. [\#797](https://github.com/redis/hiredis/pull/797) ([michael-grunder](https://github.com/michael-grunder)) -- Attempt to fix compilation on Solaris [\#796](https://github.com/redis/hiredis/pull/796) ([michael-grunder](https://github.com/michael-grunder)) -- Support timeouts in libev adapater [\#795](https://github.com/redis/hiredis/pull/795) ([michael-grunder](https://github.com/michael-grunder)) -- Fix pkgconfig when installing to a custom lib dir [\#793](https://github.com/redis/hiredis/pull/793) ([michael-grunder](https://github.com/michael-grunder)) -- Fix USE\_SSL=1 make/cmake on OSX and CMake tests [\#789](https://github.com/redis/hiredis/pull/789) ([michael-grunder](https://github.com/michael-grunder)) -- Use correct libuv call on Windows [\#784](https://github.com/redis/hiredis/pull/784) ([michael-grunder](https://github.com/michael-grunder)) -- Added CMake package config and fixed hiredis\_ssl on Windows [\#783](https://github.com/redis/hiredis/pull/783) ([michael-grunder](https://github.com/michael-grunder)) -- CMake: Set hiredis\_ssl shared object version. [\#780](https://github.com/redis/hiredis/pull/780) ([yossigo](https://github.com/yossigo)) -- Win32 tests and timeout fix [\#776](https://github.com/redis/hiredis/pull/776) ([michael-grunder](https://github.com/michael-grunder)) -- Provides an optional cleanup callback for async data. [\#768](https://github.com/redis/hiredis/pull/768) ([heronr](https://github.com/heronr)) -- Housekeeping fixes [\#764](https://github.com/redis/hiredis/pull/764) ([michael-grunder](https://github.com/michael-grunder)) -- install alloc.h [\#756](https://github.com/redis/hiredis/pull/756) ([ch1aki](https://github.com/ch1aki)) -- fix spelling mistakes [\#746](https://github.com/redis/hiredis/pull/746) ([ShooterIT](https://github.com/ShooterIT)) -- Free the reply in redisGetReply when passed NULL [\#741](https://github.com/redis/hiredis/pull/741) ([michael-grunder](https://github.com/michael-grunder)) -- Fix dead code in sslLogCallback relating to should\_log variable. [\#737](https://github.com/redis/hiredis/pull/737) ([natoscott](https://github.com/natoscott)) -- Fix typo in dict.c. [\#731](https://github.com/redis/hiredis/pull/731) ([Kevin-Xi](https://github.com/Kevin-Xi)) -- Adding an option to DISABLE\_TESTS [\#727](https://github.com/redis/hiredis/pull/727) ([pbotros](https://github.com/pbotros)) -- Update README with SSL support. [\#720](https://github.com/redis/hiredis/pull/720) ([yossigo](https://github.com/yossigo)) -- Fixes leaks in unit tests [\#715](https://github.com/redis/hiredis/pull/715) ([michael-grunder](https://github.com/michael-grunder)) -- SSL Tests [\#711](https://github.com/redis/hiredis/pull/711) ([yossigo](https://github.com/yossigo)) -- SSL Reorganization [\#708](https://github.com/redis/hiredis/pull/708) ([yossigo](https://github.com/yossigo)) -- Fix MSVC build. [\#706](https://github.com/redis/hiredis/pull/706) ([yossigo](https://github.com/yossigo)) -- SSL: Properly report SSL\_connect\(\) errors. [\#702](https://github.com/redis/hiredis/pull/702) ([yossigo](https://github.com/yossigo)) -- Silent SSL trace to stdout by default. [\#699](https://github.com/redis/hiredis/pull/699) ([yossigo](https://github.com/yossigo)) -- Port RESP3 support from Redis. [\#697](https://github.com/redis/hiredis/pull/697) ([yossigo](https://github.com/yossigo)) -- Removed whitespace before newline [\#691](https://github.com/redis/hiredis/pull/691) ([Miniwoffer](https://github.com/Miniwoffer)) -- Add install adapters header files [\#688](https://github.com/redis/hiredis/pull/688) ([kevin1018](https://github.com/kevin1018)) -- Remove unnecessary null check before free [\#684](https://github.com/redis/hiredis/pull/684) ([qlyoung](https://github.com/qlyoung)) -- redisReaderGetReply leak memory [\#671](https://github.com/redis/hiredis/pull/671) ([movebean](https://github.com/movebean)) -- fix timeout code in windows [\#670](https://github.com/redis/hiredis/pull/670) ([jman-krafton](https://github.com/jman-krafton)) -- test: fix errstr matching for musl libc [\#665](https://github.com/redis/hiredis/pull/665) ([ghost](https://github.com/ghost)) -- Windows: MinGW fixes and Windows Travis builders [\#663](https://github.com/redis/hiredis/pull/663) ([mbitsnbites](https://github.com/mbitsnbites)) -- The setsockopt and getsockopt API diffs from BSD socket and WSA one [\#662](https://github.com/redis/hiredis/pull/662) ([dragonation](https://github.com/dragonation)) -- Fix Compile Error On Windows \(Visual Studio\) [\#658](https://github.com/redis/hiredis/pull/658) ([jinjiazhang](https://github.com/jinjiazhang)) -- Fix NXDOMAIN test case [\#653](https://github.com/redis/hiredis/pull/653) ([michael-grunder](https://github.com/michael-grunder)) -- Add MinGW support [\#652](https://github.com/redis/hiredis/pull/652) ([mbitsnbites](https://github.com/mbitsnbites)) -- SSL Support [\#645](https://github.com/redis/hiredis/pull/645) ([mnunberg](https://github.com/mnunberg)) -- Fix Invalid argument after redisAsyncConnectUnix [\#644](https://github.com/redis/hiredis/pull/644) ([codehz](https://github.com/codehz)) -- Makefile: use predefined AR [\#632](https://github.com/redis/hiredis/pull/632) ([Mic92](https://github.com/Mic92)) -- FreeBSD build fix [\#628](https://github.com/redis/hiredis/pull/628) ([devnexen](https://github.com/devnexen)) -- Fix errors not propagating properly with libuv.h. [\#624](https://github.com/redis/hiredis/pull/624) ([yossigo](https://github.com/yossigo)) -- Update README.md [\#621](https://github.com/redis/hiredis/pull/621) ([Crunsher](https://github.com/Crunsher)) -- Fix redisBufferRead documentation [\#620](https://github.com/redis/hiredis/pull/620) ([hacst](https://github.com/hacst)) -- Add CPPFLAGS to REAL\_CFLAGS [\#614](https://github.com/redis/hiredis/pull/614) ([thomaslee](https://github.com/thomaslee)) -- Update createArray to take size\_t [\#597](https://github.com/redis/hiredis/pull/597) ([justinbrewer](https://github.com/justinbrewer)) -- fix common realloc mistake and add null check more [\#580](https://github.com/redis/hiredis/pull/580) ([charsyam](https://github.com/charsyam)) -- Proper error reporting for connect failures [\#578](https://github.com/redis/hiredis/pull/578) ([mnunberg](https://github.com/mnunberg)) - -\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* - -## [1.0.0-rc1](https://github.com/redis/hiredis/tree/v1.0.0-rc1) - (2020-07-29) - -_Note: There were no changes to code between v1.0.0-rc1 and v1.0.0 so see v1.0.0 for changelog_ - -### 0.14.1 (2020-03-13) - -* Adds safe allocation wrappers (CVE-2020-7105, #747, #752) (Michael Grunder) - -### 0.14.0 (2018-09-25) -**BREAKING CHANGES**: - -* Change `redisReply.len` to `size_t`, as it denotes the the size of a string - - User code should compare this to `size_t` values as well. - If it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before. - -* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b]) -* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537]) -* Use string2ll from Redis w/added tests (Michael Grunder [7bef04, 60f622]) -* Makefile - OSX compilation fixes (Ryan Schmidt [881fcb, 0e9af8]) -* Remove redundant NULL checks (Justin Brewer [54acc8, 58e6b8]) -* Fix bulk and multi-bulk length truncation (Justin Brewer [109197]) -* Fix SIGSEGV in OpenBSD by checking for NULL before calling freeaddrinfo (Justin Brewer [546d94]) -* Several POSIX compatibility fixes (Justin Brewer [bbeab8, 49bbaa, d1c1b6]) -* Makefile - Compatibility fixes (Dimitri Vorobiev [3238cf, 12a9d1]) -* Makefile - Fix make install on FreeBSD (Zach Shipko [a2ef2b]) -* Makefile - don't assume $(INSTALL) is cp (Igor Gnatenko [725a96]) -* Separate side-effect causing function from assert and small cleanup (amallia [b46413, 3c3234]) -* Don't send negative values to `__redisAsyncCommand` (Frederik Deweerdt [706129]) -* Fix leak if setsockopt fails (Frederik Deweerdt [e21c9c]) -* Fix libevent leak (zfz [515228]) -* Clean up GCC warning (Ichito Nagata [2ec774]) -* Keep track of errno in `__redisSetErrorFromErrno()` as snprintf may use it (Jin Qing [25cd88]) -* Solaris compilation fix (Donald Whyte [41b07d]) -* Reorder linker arguments when building examples (Tustfarm-heart [06eedd]) -* Keep track of subscriptions in case of rapid subscribe/unsubscribe (Hyungjin Kim [073dc8, be76c5, d46999]) -* libuv use after free fix (Paul Scott [cbb956]) -* Properly close socket fd on reconnect attempt (WSL [64d1ec]) -* Skip valgrind in OSX tests (Jan-Erik Rediger [9deb78]) -* Various updates for Travis testing OSX (Ted Nyman [fa3774, 16a459, bc0ea5]) -* Update libevent (Chris Xin [386802]) -* Change sds.h for building in C++ projects (Ali Volkan ATLI [f5b32e]) -* Use proper format specifier in redisFormatSdsCommandArgv (Paulino Huerta, Jan-Erik Rediger [360a06, 8655a6]) -* Better handling of NULL reply in example code (Jan-Erik Rediger [1b8ed3]) -* Prevent overflow when formatting an error (Jan-Erik Rediger [0335cb]) -* Compatibility fix for strerror_r (Tom Lee [bb1747]) -* Properly detect integer parse/overflow errors (Justin Brewer [93421f]) -* Adds CI for Windows and cygwin fixes (owent, [6c53d6, 6c3e40]) -* Catch a buffer overflow when formatting the error message -* Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13 -* Fix warnings, when compiled with -Wshadow -* Make hiredis compile in Cygwin on Windows, now CI-tested -* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now - protocol errors. This is consistent with the RESP specification. On 32-bit - platforms, the upper bound is lowered to `SIZE_MAX`. - -* Remove backwards compatibility macro's - -This removes the following old function aliases, use the new name now: - -| Old | New | -| --------------------------- | ---------------------- | -| redisReplyReaderCreate | redisReaderCreate | -| redisReplyReaderCreate | redisReaderCreate | -| redisReplyReaderFree | redisReaderFree | -| redisReplyReaderFeed | redisReaderFeed | -| redisReplyReaderGetReply | redisReaderGetReply | -| redisReplyReaderSetPrivdata | redisReaderSetPrivdata | -| redisReplyReaderGetObject | redisReaderGetObject | -| redisReplyReaderGetError | redisReaderGetError | - -* The `DEBUG` variable in the Makefile was renamed to `DEBUG_FLAGS` - -Previously it broke some builds for people that had `DEBUG` set to some arbitrary value, -due to debugging other software. -By renaming we avoid unintentional name clashes. - -Simply rename `DEBUG` to `DEBUG_FLAGS` in your environment to make it working again. - -### 0.13.3 (2015-09-16) - -* Revert "Clear `REDIS_CONNECTED` flag when connection is closed". -* Make tests pass on FreeBSD (Thanks, Giacomo Olgeni) - - -If the `REDIS_CONNECTED` flag is cleared, -the async onDisconnect callback function will never be called. -This causes problems as the disconnect is never reported back to the user. - -### 0.13.2 (2015-08-25) - -* Prevent crash on pending replies in async code (Thanks, @switch-st) -* Clear `REDIS_CONNECTED` flag when connection is closed (Thanks, Jerry Jacobs) -* Add MacOS X addapter (Thanks, @dizzus) -* Add Qt adapter (Thanks, Pietro Cerutti) -* Add Ivykis adapter (Thanks, Gergely Nagy) - -All adapters are provided as is and are only tested where possible. - -### 0.13.1 (2015-05-03) - -This is a bug fix release. -The new `reconnect` method introduced new struct members, which clashed with pre-defined names in pre-C99 code. -Another commit forced C99 compilation just to make it work, but of course this is not desirable for outside projects. -Other non-C99 code can now use hiredis as usual again. -Sorry for the inconvenience. - -* Fix memory leak in async reply handling (Salvatore Sanfilippo) -* Rename struct member to avoid name clash with pre-c99 code (Alex Balashov, ncopa) - -### 0.13.0 (2015-04-16) - -This release adds a minimal Windows compatibility layer. -The parser, standalone since v0.12.0, can now be compiled on Windows -(and thus used in other client libraries as well) - -* Windows compatibility layer for parser code (tzickel) -* Properly escape data printed to PKGCONF file (Dan Skorupski) -* Fix tests when assert() undefined (Keith Bennett, Matt Stancliff) -* Implement a reconnect method for the client context, this changes the structure of `redisContext` (Aaron Bedra) - -### 0.12.1 (2015-01-26) - -* Fix `make install`: DESTDIR support, install all required files, install PKGCONF in proper location -* Fix `make test` as 32 bit build on 64 bit platform - -### 0.12.0 (2015-01-22) - -* Add optional KeepAlive support - -* Try again on EINTR errors - -* Add libuv adapter - -* Add IPv6 support - -* Remove possibility of multiple close on same fd - -* Add ability to bind source address on connect - -* Add redisConnectFd() and redisFreeKeepFd() - -* Fix getaddrinfo() memory leak - -* Free string if it is unused (fixes memory leak) - -* Improve redisAppendCommandArgv performance 2.5x - -* Add support for SO_REUSEADDR - -* Fix redisvFormatCommand format parsing - -* Add GLib 2.0 adapter - -* Refactor reading code into read.c - -* Fix errno error buffers to not clobber errors - -* Generate pkgconf during build - -* Silence _BSD_SOURCE warnings - -* Improve digit counting for multibulk creation - - -### 0.11.0 - -* Increase the maximum multi-bulk reply depth to 7. - -* Increase the read buffer size from 2k to 16k. - -* Use poll(2) instead of select(2) to support large fds (>= 1024). - -### 0.10.1 - -* Makefile overhaul. Important to check out if you override one or more - variables using environment variables or via arguments to the "make" tool. - -* Issue #45: Fix potential memory leak for a multi bulk reply with 0 elements - being created by the default reply object functions. - -* Issue #43: Don't crash in an asynchronous context when Redis returns an error - reply after the connection has been made (this happens when the maximum - number of connections is reached). - -### 0.10.0 - -* See commit log. diff --git a/ext/hiredis-1.0.2/CMakeLists.txt b/ext/hiredis-1.0.2/CMakeLists.txt deleted file mode 100644 index f86c9b70b..000000000 --- a/ext/hiredis-1.0.2/CMakeLists.txt +++ /dev/null @@ -1,165 +0,0 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.4.0) -INCLUDE(GNUInstallDirs) -PROJECT(hiredis) - -OPTION(ENABLE_SSL "Build hiredis_ssl for SSL support" OFF) -OPTION(DISABLE_TESTS "If tests should be compiled or not" OFF) -OPTION(ENABLE_SSL_TESTS, "Should we test SSL connections" OFF) - -MACRO(getVersionBit name) - SET(VERSION_REGEX "^#define ${name} (.+)$") - FILE(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/hiredis.h" - VERSION_BIT REGEX ${VERSION_REGEX}) - STRING(REGEX REPLACE ${VERSION_REGEX} "\\1" ${name} "${VERSION_BIT}") -ENDMACRO(getVersionBit) - -getVersionBit(HIREDIS_MAJOR) -getVersionBit(HIREDIS_MINOR) -getVersionBit(HIREDIS_PATCH) -getVersionBit(HIREDIS_SONAME) -SET(VERSION "${HIREDIS_MAJOR}.${HIREDIS_MINOR}.${HIREDIS_PATCH}") -MESSAGE("Detected version: ${VERSION}") - -PROJECT(hiredis VERSION "${VERSION}") - -SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples") - -SET(hiredis_sources - alloc.c - async.c - dict.c - hiredis.c - net.c - read.c - sds.c - sockcompat.c) - -SET(hiredis_sources ${hiredis_sources}) - -IF(WIN32) - ADD_COMPILE_DEFINITIONS(_CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN) -ENDIF() - -ADD_LIBRARY(hiredis SHARED ${hiredis_sources}) - -SET_TARGET_PROPERTIES(hiredis - PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE - VERSION "${HIREDIS_SONAME}") -IF(WIN32 OR MINGW) - TARGET_LINK_LIBRARIES(hiredis PRIVATE ws2_32) -ENDIF() - -TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC $ $) - -CONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY) - -INSTALL(TARGETS hiredis - EXPORT hiredis-targets - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - -INSTALL(FILES hiredis.h read.h sds.h async.h alloc.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis) - -INSTALL(DIRECTORY adapters - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis) - -INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis.pc - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) - -export(EXPORT hiredis-targets - FILE "${CMAKE_CURRENT_BINARY_DIR}/hiredis-targets.cmake" - NAMESPACE hiredis::) - -SET(CMAKE_CONF_INSTALL_DIR share/hiredis) -SET(INCLUDE_INSTALL_DIR include) -include(CMakePackageConfigHelpers) -configure_package_config_file(hiredis-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/hiredis-config.cmake - INSTALL_DESTINATION ${CMAKE_CONF_INSTALL_DIR} - PATH_VARS INCLUDE_INSTALL_DIR) - -INSTALL(EXPORT hiredis-targets - FILE hiredis-targets.cmake - NAMESPACE hiredis:: - DESTINATION ${CMAKE_CONF_INSTALL_DIR}) - -INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis-config.cmake - DESTINATION ${CMAKE_CONF_INSTALL_DIR}) - - -IF(ENABLE_SSL) - IF (NOT OPENSSL_ROOT_DIR) - IF (APPLE) - SET(OPENSSL_ROOT_DIR "/usr/local/opt/openssl") - ENDIF() - ENDIF() - FIND_PACKAGE(OpenSSL REQUIRED) - SET(hiredis_ssl_sources - ssl.c) - ADD_LIBRARY(hiredis_ssl SHARED - ${hiredis_ssl_sources}) - - IF (APPLE) - SET_PROPERTY(TARGET hiredis_ssl PROPERTY LINK_FLAGS "-Wl,-undefined -Wl,dynamic_lookup") - ENDIF() - - SET_TARGET_PROPERTIES(hiredis_ssl - PROPERTIES - WINDOWS_EXPORT_ALL_SYMBOLS TRUE - VERSION "${HIREDIS_SONAME}") - - TARGET_INCLUDE_DIRECTORIES(hiredis_ssl PRIVATE "${OPENSSL_INCLUDE_DIR}") - TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE ${OPENSSL_LIBRARIES}) - IF (WIN32 OR MINGW) - TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE hiredis) - ENDIF() - CONFIGURE_FILE(hiredis_ssl.pc.in hiredis_ssl.pc @ONLY) - - INSTALL(TARGETS hiredis_ssl - EXPORT hiredis_ssl-targets - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - - INSTALL(FILES hiredis_ssl.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis) - - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl.pc - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) - - export(EXPORT hiredis_ssl-targets - FILE "${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-targets.cmake" - NAMESPACE hiredis::) - - SET(CMAKE_CONF_INSTALL_DIR share/hiredis_ssl) - configure_package_config_file(hiredis_ssl-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-config.cmake - INSTALL_DESTINATION ${CMAKE_CONF_INSTALL_DIR} - PATH_VARS INCLUDE_INSTALL_DIR) - - INSTALL(EXPORT hiredis_ssl-targets - FILE hiredis_ssl-targets.cmake - NAMESPACE hiredis:: - DESTINATION ${CMAKE_CONF_INSTALL_DIR}) - - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-config.cmake - DESTINATION ${CMAKE_CONF_INSTALL_DIR}) -ENDIF() - -IF(NOT DISABLE_TESTS) - ENABLE_TESTING() - ADD_EXECUTABLE(hiredis-test test.c) - IF(ENABLE_SSL_TESTS) - ADD_DEFINITIONS(-DHIREDIS_TEST_SSL=1) - TARGET_LINK_LIBRARIES(hiredis-test hiredis hiredis_ssl) - ELSE() - TARGET_LINK_LIBRARIES(hiredis-test hiredis) - ENDIF() - ADD_TEST(NAME hiredis-test - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh) -ENDIF() - -# Add examples -IF(ENABLE_EXAMPLES) - ADD_SUBDIRECTORY(examples) -ENDIF(ENABLE_EXAMPLES) diff --git a/ext/hiredis-1.0.2/COPYING b/ext/hiredis-1.0.2/COPYING deleted file mode 100644 index a5fc97395..000000000 --- a/ext/hiredis-1.0.2/COPYING +++ /dev/null @@ -1,29 +0,0 @@ -Copyright (c) 2009-2011, Salvatore Sanfilippo -Copyright (c) 2010-2011, Pieter Noordhuis - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of Redis nor the names of its contributors may be used - to endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ext/hiredis-1.0.2/Makefile b/ext/hiredis-1.0.2/Makefile deleted file mode 100644 index a8d37a2eb..000000000 --- a/ext/hiredis-1.0.2/Makefile +++ /dev/null @@ -1,308 +0,0 @@ -# Hiredis Makefile -# Copyright (C) 2010-2011 Salvatore Sanfilippo -# Copyright (C) 2010-2011 Pieter Noordhuis -# This file is released under the BSD license, see the COPYING file - -OBJ=alloc.o net.o hiredis.o sds.o async.o read.o sockcompat.o -SSL_OBJ=ssl.o -EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib hiredis-example-push -ifeq ($(USE_SSL),1) -EXAMPLES+=hiredis-example-ssl hiredis-example-libevent-ssl -endif -TESTS=hiredis-test -LIBNAME=libhiredis -PKGCONFNAME=hiredis.pc -SSL_LIBNAME=libhiredis_ssl -SSL_PKGCONFNAME=hiredis_ssl.pc - -HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}') -HIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}') -HIREDIS_PATCH=$(shell grep HIREDIS_PATCH hiredis.h | awk '{print $$3}') -HIREDIS_SONAME=$(shell grep HIREDIS_SONAME hiredis.h | awk '{print $$3}') - -# Installation related variables and target -PREFIX?=/usr/local -INCLUDE_PATH?=include/hiredis -LIBRARY_PATH?=lib -PKGCONF_PATH?=pkgconfig -INSTALL_INCLUDE_PATH= $(DESTDIR)$(PREFIX)/$(INCLUDE_PATH) -INSTALL_LIBRARY_PATH= $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH) -INSTALL_PKGCONF_PATH= $(INSTALL_LIBRARY_PATH)/$(PKGCONF_PATH) - -# redis-server configuration used for testing -REDIS_PORT=56379 -REDIS_SERVER=redis-server -define REDIS_TEST_CONFIG - daemonize yes - pidfile /tmp/hiredis-test-redis.pid - port $(REDIS_PORT) - bind 127.0.0.1 - unixsocket /tmp/hiredis-test-redis.sock -endef -export REDIS_TEST_CONFIG - -# Fallback to gcc when $CC is not in $PATH. -CC:=$(shell sh -c 'type $${CC%% *} >/dev/null 2>/dev/null && echo $(CC) || echo gcc') -CXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++') -OPTIMIZATION?=-O3 -WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers -DEBUG_FLAGS?= -g -ggdb -REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS) -REAL_LDFLAGS=$(LDFLAGS) - -DYLIBSUFFIX=so -STLIBSUFFIX=a -DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME) -DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR) -DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX) - -DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -STLIBNAME=$(LIBNAME).$(STLIBSUFFIX) -STLIB_MAKE_CMD=$(AR) rcs - -SSL_DYLIB_MINOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME) -SSL_DYLIB_MAJOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR) -SSL_DYLIBNAME=$(SSL_LIBNAME).$(DYLIBSUFFIX) -SSL_STLIBNAME=$(SSL_LIBNAME).$(STLIBSUFFIX) -SSL_DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(SSL_DYLIB_MINOR_NAME) - -# Platform-specific overrides -uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') - -USE_SSL?=0 - -# This is required for test.c only -ifeq ($(USE_SSL),1) - CFLAGS+=-DHIREDIS_TEST_SSL -endif - -ifeq ($(uname_S),Linux) - SSL_LDFLAGS=-lssl -lcrypto -else - OPENSSL_PREFIX?=/usr/local/opt/openssl - CFLAGS+=-I$(OPENSSL_PREFIX)/include - SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto -endif - -ifeq ($(uname_S),SunOS) - IS_SUN_CC=$(shell sh -c '$(CC) -V 2>&1 |egrep -i -c "sun|studio"') - ifeq ($(IS_SUN_CC),1) - SUN_SHARED_FLAG=-G - else - SUN_SHARED_FLAG=-shared - endif - REAL_LDFLAGS+= -ldl -lnsl -lsocket - DYLIB_MAKE_CMD=$(CC) $(SUN_SHARED_FLAG) -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS) - SSL_DYLIB_MAKE_CMD=$(CC) $(SUN_SHARED_FLAG) -o $(SSL_DYLIBNAME) -h $(SSL_DYLIB_MINOR_NAME) $(LDFLAGS) $(SSL_LDFLAGS) -endif -ifeq ($(uname_S),Darwin) - DYLIBSUFFIX=dylib - DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX) - DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS) - SSL_DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(SSL_DYLIB_MINOR_NAME) -o $(SSL_DYLIBNAME) $(LDFLAGS) $(SSL_LDFLAGS) - DYLIB_PLUGIN=-Wl,-undefined -Wl,dynamic_lookup -endif - -all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME) -ifeq ($(USE_SSL),1) -all: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME) -endif - -# Deps (use make dep to generate this) -alloc.o: alloc.c fmacros.h alloc.h -async.o: async.c fmacros.h alloc.h async.h hiredis.h read.h sds.h net.h dict.c dict.h win32.h async_private.h -dict.o: dict.c fmacros.h alloc.h dict.h -hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h alloc.h net.h async.h win32.h -net.o: net.c fmacros.h net.h hiredis.h read.h sds.h alloc.h sockcompat.h win32.h -read.o: read.c fmacros.h alloc.h read.h sds.h win32.h -sds.o: sds.c sds.h sdsalloc.h alloc.h -sockcompat.o: sockcompat.c sockcompat.h -ssl.o: ssl.c hiredis.h read.h sds.h alloc.h async.h win32.h async_private.h -test.o: test.c fmacros.h hiredis.h read.h sds.h alloc.h net.h sockcompat.h win32.h - -$(DYLIBNAME): $(OBJ) - $(DYLIB_MAKE_CMD) -o $(DYLIBNAME) $(OBJ) $(REAL_LDFLAGS) - -$(STLIBNAME): $(OBJ) - $(STLIB_MAKE_CMD) $(STLIBNAME) $(OBJ) - -$(SSL_DYLIBNAME): $(SSL_OBJ) - $(SSL_DYLIB_MAKE_CMD) $(DYLIB_PLUGIN) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(LDFLAGS) $(SSL_LDFLAGS) - -$(SSL_STLIBNAME): $(SSL_OBJ) - $(STLIB_MAKE_CMD) $(SSL_STLIBNAME) $(SSL_OBJ) - -dynamic: $(DYLIBNAME) -static: $(STLIBNAME) -ifeq ($(USE_SSL),1) -dynamic: $(SSL_DYLIBNAME) -static: $(SSL_STLIBNAME) -endif - -# Binaries: -hiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(REAL_LDFLAGS) - -hiredis-example-libevent-ssl: examples/example-libevent-ssl.c adapters/libevent.h $(STLIBNAME) $(SSL_STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS) - -hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -lev $(STLIBNAME) $(REAL_LDFLAGS) - -hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME) $(REAL_LDFLAGS) - -hiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -livykis $(STLIBNAME) $(REAL_LDFLAGS) - -hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -framework CoreFoundation $(STLIBNAME) $(REAL_LDFLAGS) - -hiredis-example-ssl: examples/example-ssl.c $(STLIBNAME) $(SSL_STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS) - - -ifndef AE_DIR -hiredis-example-ae: - @echo "Please specify AE_DIR (e.g. /src)" - @false -else -hiredis-example-ae: examples/example-ae.c adapters/ae.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(AE_DIR) $< $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o $(AE_DIR)/../deps/jemalloc/lib/libjemalloc.a -pthread $(STLIBNAME) -endif - -ifndef LIBUV_DIR -hiredis-example-libuv: - @echo "Please specify LIBUV_DIR (e.g. ../libuv/)" - @false -else -hiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread -lrt $(STLIBNAME) $(REAL_LDFLAGS) -endif - -ifeq ($(and $(QT_MOC),$(QT_INCLUDE_DIR),$(QT_LIBRARY_DIR)),) -hiredis-example-qt: - @echo "Please specify QT_MOC, QT_INCLUDE_DIR AND QT_LIBRARY_DIR" - @false -else -hiredis-example-qt: examples/example-qt.cpp adapters/qt.h $(STLIBNAME) - $(QT_MOC) adapters/qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \ - $(CXX) -x c++ -o qt-adapter-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore - $(QT_MOC) examples/example-qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \ - $(CXX) -x c++ -o qt-example-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore - $(CXX) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore -L$(QT_LIBRARY_DIR) qt-adapter-moc.o qt-example-moc.o $< -pthread $(STLIBNAME) -lQtCore -endif - -hiredis-example: examples/example.c $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS) - -hiredis-example-push: examples/example-push.c $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS) - -examples: $(EXAMPLES) - -TEST_LIBS = $(STLIBNAME) -ifeq ($(USE_SSL),1) - TEST_LIBS += $(SSL_STLIBNAME) - TEST_LDFLAGS = $(SSL_LDFLAGS) -lssl -lcrypto -lpthread -endif - -hiredis-test: test.o $(TEST_LIBS) - $(CC) -o $@ $(REAL_CFLAGS) -I. $^ $(REAL_LDFLAGS) $(TEST_LDFLAGS) - -hiredis-%: %.o $(STLIBNAME) - $(CC) $(REAL_CFLAGS) -o $@ $< $(TEST_LIBS) $(REAL_LDFLAGS) - -test: hiredis-test - ./hiredis-test - -check: hiredis-test - TEST_SSL=$(USE_SSL) ./test.sh - -.c.o: - $(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $< - -clean: - rm -rf $(DYLIBNAME) $(STLIBNAME) $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov - -dep: - $(CC) $(CPPFLAGS) $(CFLAGS) -MM *.c - -INSTALL?= cp -pPR - -$(PKGCONFNAME): hiredis.h - @echo "Generating $@ for pkgconfig..." - @echo prefix=$(PREFIX) > $@ - @echo exec_prefix=\$${prefix} >> $@ - @echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@ - @echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@ - @echo >> $@ - @echo Name: hiredis >> $@ - @echo Description: Minimalistic C client library for Redis. >> $@ - @echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@ - @echo Libs: -L\$${libdir} -lhiredis >> $@ - @echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@ - -$(SSL_PKGCONFNAME): hiredis_ssl.h - @echo "Generating $@ for pkgconfig..." - @echo prefix=$(PREFIX) > $@ - @echo exec_prefix=\$${prefix} >> $@ - @echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@ - @echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@ - @echo >> $@ - @echo Name: hiredis_ssl >> $@ - @echo Description: SSL Support for hiredis. >> $@ - @echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@ - @echo Requires: hiredis >> $@ - @echo Libs: -L\$${libdir} -lhiredis_ssl >> $@ - @echo Libs.private: -lssl -lcrypto >> $@ - -install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME) - mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH) - $(INSTALL) hiredis.h async.h read.h sds.h alloc.h $(INSTALL_INCLUDE_PATH) - $(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters - $(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME) - cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME) - $(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH) - mkdir -p $(INSTALL_PKGCONF_PATH) - $(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH) - -ifeq ($(USE_SSL),1) -install: install-ssl - -install-ssl: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME) - mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH) - $(INSTALL) hiredis_ssl.h $(INSTALL_INCLUDE_PATH) - $(INSTALL) $(SSL_DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(SSL_DYLIB_MINOR_NAME) - cd $(INSTALL_LIBRARY_PATH) && ln -sf $(SSL_DYLIB_MINOR_NAME) $(SSL_DYLIBNAME) - $(INSTALL) $(SSL_STLIBNAME) $(INSTALL_LIBRARY_PATH) - mkdir -p $(INSTALL_PKGCONF_PATH) - $(INSTALL) $(SSL_PKGCONFNAME) $(INSTALL_PKGCONF_PATH) -endif - -32bit: - @echo "" - @echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386" - @echo "" - $(MAKE) CFLAGS="-m32" LDFLAGS="-m32" - -32bit-vars: - $(eval CFLAGS=-m32) - $(eval LDFLAGS=-m32) - -gprof: - $(MAKE) CFLAGS="-pg" LDFLAGS="-pg" - -gcov: - $(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs" - -coverage: gcov - make check - mkdir -p tmp/lcov - lcov -d . -c -o tmp/lcov/hiredis.info - genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info - -noopt: - $(MAKE) OPTIMIZATION="" - -.PHONY: all test check clean dep install 32bit 32bit-vars gprof gcov noopt diff --git a/ext/hiredis-1.0.2/README.md b/ext/hiredis-1.0.2/README.md deleted file mode 100644 index c544d5718..000000000 --- a/ext/hiredis-1.0.2/README.md +++ /dev/null @@ -1,664 +0,0 @@ -[![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis) - -**This Readme reflects the latest changed in the master branch. See [v1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) for the Readme and documentation for the latest release ([API/ABI history](https://abi-laboratory.pro/?view=timeline&l=hiredis)).** - -# HIREDIS - -Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) database. - -It is minimalistic because it just adds minimal support for the protocol, but -at the same time it uses a high level printf-alike API in order to make it -much higher level than otherwise suggested by its minimal code base and the -lack of explicit bindings for every Redis command. - -Apart from supporting sending commands and receiving replies, it comes with -a reply parser that is decoupled from the I/O layer. It -is a stream parser designed for easy reusability, which can for instance be used -in higher level language bindings for efficient reply parsing. - -Hiredis only supports the binary-safe Redis protocol, so you can use it with any -Redis version >= 1.2.0. - -The library comes with multiple APIs. There is the -*synchronous API*, the *asynchronous API* and the *reply parsing API*. - -## Upgrading to `1.0.2` - -NOTE: v1.0.1 erroneously bumped SONAME, which is why it is skipped here. - -Version 1.0.2 is simply 1.0.0 with a fix for [CVE-2021-32765](https://github.com/redis/hiredis/security/advisories/GHSA-hfm9-39pp-55p2). They are otherwise identical. - -## Upgrading to `1.0.0` - -Version 1.0.0 marks the first stable release of Hiredis. -It includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory. -It also bundles the updated `sds` library, to sync up with upstream and Redis. -For code changes see the [Changelog](CHANGELOG.md). - -_Note: As described below, a few member names have been changed but most applications should be able to upgrade with minor code changes and recompiling._ - -## IMPORTANT: Breaking changes from `0.14.1` -> `1.0.0` - -* `redisContext` has two additional members (`free_privdata`, and `privctx`). -* `redisOptions.timeout` has been renamed to `redisOptions.connect_timeout`, and we've added `redisOptions.command_timeout`. -* `redisReplyObjectFunctions.createArray` now takes `size_t` instead of `int` for its length parameter. - -## IMPORTANT: Breaking changes when upgrading from 0.13.x -> 0.14.x - -Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now -protocol errors. This is consistent with the RESP specification. On 32-bit -platforms, the upper bound is lowered to `SIZE_MAX`. - -Change `redisReply.len` to `size_t`, as it denotes the the size of a string - -User code should compare this to `size_t` values as well. If it was used to -compare to other values, casting might be necessary or can be removed, if -casting was applied before. - -## Upgrading from `<0.9.0` - -Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing -code using hiredis should not be a big pain. The key thing to keep in mind when -upgrading is that hiredis >= 0.9.0 uses a `redisContext*` to keep state, in contrast to -the stateless 0.0.1 that only has a file descriptor to work with. - -## Synchronous API - -To consume the synchronous API, there are only a few function calls that need to be introduced: - -```c -redisContext *redisConnect(const char *ip, int port); -void *redisCommand(redisContext *c, const char *format, ...); -void freeReplyObject(void *reply); -``` - -### Connecting - -The function `redisConnect` is used to create a so-called `redisContext`. The -context is where Hiredis holds state for a connection. The `redisContext` -struct has an integer `err` field that is non-zero when the connection is in -an error state. The field `errstr` will contain a string with a description of -the error. More information on errors can be found in the **Errors** section. -After trying to connect to Redis using `redisConnect` you should -check the `err` field to see if establishing the connection was successful: -```c -redisContext *c = redisConnect("127.0.0.1", 6379); -if (c == NULL || c->err) { - if (c) { - printf("Error: %s\n", c->errstr); - // handle error - } else { - printf("Can't allocate redis context\n"); - } -} -``` - -*Note: A `redisContext` is not thread-safe.* - -### Sending commands - -There are several ways to issue commands to Redis. The first that will be introduced is -`redisCommand`. This function takes a format similar to printf. In the simplest form, -it is used like this: -```c -reply = redisCommand(context, "SET foo bar"); -``` - -The specifier `%s` interpolates a string in the command, and uses `strlen` to -determine the length of the string: -```c -reply = redisCommand(context, "SET foo %s", value); -``` -When you need to pass binary safe strings in a command, the `%b` specifier can be -used. Together with a pointer to the string, it requires a `size_t` length argument -of the string: -```c -reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen); -``` -Internally, Hiredis splits the command in different arguments and will -convert it to the protocol used to communicate with Redis. -One or more spaces separates arguments, so you can use the specifiers -anywhere in an argument: -```c -reply = redisCommand(context, "SET key:%s %s", myid, value); -``` - -### Using replies - -The return value of `redisCommand` holds a reply when the command was -successfully executed. When an error occurs, the return value is `NULL` and -the `err` field in the context will be set (see section on **Errors**). -Once an error is returned the context cannot be reused and you should set up -a new connection. - -The standard replies that `redisCommand` are of the type `redisReply`. The -`type` field in the `redisReply` should be used to test what kind of reply -was received: - -### RESP2 - -* **`REDIS_REPLY_STATUS`**: - * The command replied with a status reply. The status string can be accessed using `reply->str`. - The length of this string can be accessed using `reply->len`. - -* **`REDIS_REPLY_ERROR`**: - * The command replied with an error. The error string can be accessed identical to `REDIS_REPLY_STATUS`. - -* **`REDIS_REPLY_INTEGER`**: - * The command replied with an integer. The integer value can be accessed using the - `reply->integer` field of type `long long`. - -* **`REDIS_REPLY_NIL`**: - * The command replied with a **nil** object. There is no data to access. - -* **`REDIS_REPLY_STRING`**: - * A bulk (string) reply. The value of the reply can be accessed using `reply->str`. - The length of this string can be accessed using `reply->len`. - -* **`REDIS_REPLY_ARRAY`**: - * A multi bulk reply. The number of elements in the multi bulk reply is stored in - `reply->elements`. Every element in the multi bulk reply is a `redisReply` object as well - and can be accessed via `reply->element[..index..]`. - Redis may reply with nested arrays but this is fully supported. - -### RESP3 - -Hiredis also supports every new `RESP3` data type which are as follows. For more information about the protocol see the `RESP3` [specification.](https://github.com/antirez/RESP3/blob/master/spec.md) - -* **`REDIS_REPLY_DOUBLE`**: - * The command replied with a double-precision floating point number. - The value is stored as a string in the `str` member, and can be converted with `strtod` or similar. - -* **`REDIS_REPLY_BOOL`**: - * A boolean true/false reply. - The value is stored in the `integer` member and will be either `0` or `1`. - -* **`REDIS_REPLY_MAP`**: - * An array with the added invariant that there will always be an even number of elements. - The MAP is functionally equivelant to `REDIS_REPLY_ARRAY` except for the previously mentioned invariant. - -* **`REDIS_REPLY_SET`**: - * An array response where each entry is unique. - Like the MAP type, the data is identical to an array response except there are no duplicate values. - -* **`REDIS_REPLY_PUSH`**: - * An array that can be generated spontaneously by Redis. - This array response will always contain at least two subelements. The first contains the type of `PUSH` message (e.g. `message`, or `invalidate`), and the second being a sub-array with the `PUSH` payload itself. - -* **`REDIS_REPLY_ATTR`**: - * An array structurally identical to a `MAP` but intended as meta-data about a reply. - _As of Redis 6.0.6 this reply type is not used in Redis_ - -* **`REDIS_REPLY_BIGNUM`**: - * A string representing an arbitrarily large signed or unsigned integer value. - The number will be encoded as a string in the `str` member of `redisReply`. - -* **`REDIS_REPLY_VERB`**: - * A verbatim string, intended to be presented to the user without modification. - The string payload is stored in the `str` memeber, and type data is stored in the `vtype` member (e.g. `txt` for raw text or `md` for markdown). - -Replies should be freed using the `freeReplyObject()` function. -Note that this function will take care of freeing sub-reply objects -contained in arrays and nested arrays, so there is no need for the user to -free the sub replies (it is actually harmful and will corrupt the memory). - -**Important:** the current version of hiredis (1.0.0) frees replies when the -asynchronous API is used. This means you should not call `freeReplyObject` when -you use this API. The reply is cleaned up by hiredis _after_ the callback -returns. We may introduce a flag to make this configurable in future versions of the library. - -### Cleaning up - -To disconnect and free the context the following function can be used: -```c -void redisFree(redisContext *c); -``` -This function immediately closes the socket and then frees the allocations done in -creating the context. - -### Sending commands (cont'd) - -Together with `redisCommand`, the function `redisCommandArgv` can be used to issue commands. -It has the following prototype: -```c -void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); -``` -It takes the number of arguments `argc`, an array of strings `argv` and the lengths of the -arguments `argvlen`. For convenience, `argvlen` may be set to `NULL` and the function will -use `strlen(3)` on every argument to determine its length. Obviously, when any of the arguments -need to be binary safe, the entire array of lengths `argvlen` should be provided. - -The return value has the same semantic as `redisCommand`. - -### Pipelining - -To explain how Hiredis supports pipelining in a blocking connection, there needs to be -understanding of the internal execution flow. - -When any of the functions in the `redisCommand` family is called, Hiredis first formats the -command according to the Redis protocol. The formatted command is then put in the output buffer -of the context. This output buffer is dynamic, so it can hold any number of commands. -After the command is put in the output buffer, `redisGetReply` is called. This function has the -following two execution paths: - -1. The input buffer is non-empty: - * Try to parse a single reply from the input buffer and return it - * If no reply could be parsed, continue at *2* -2. The input buffer is empty: - * Write the **entire** output buffer to the socket - * Read from the socket until a single reply could be parsed - -The function `redisGetReply` is exported as part of the Hiredis API and can be used when a reply -is expected on the socket. To pipeline commands, the only things that needs to be done is -filling up the output buffer. For this cause, two commands can be used that are identical -to the `redisCommand` family, apart from not returning a reply: -```c -void redisAppendCommand(redisContext *c, const char *format, ...); -void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); -``` -After calling either function one or more times, `redisGetReply` can be used to receive the -subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where -the latter means an error occurred while reading a reply. Just as with the other commands, -the `err` field in the context can be used to find out what the cause of this error is. - -The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and -a single call to `read(2)`): -```c -redisReply *reply; -redisAppendCommand(context,"SET foo bar"); -redisAppendCommand(context,"GET foo"); -redisGetReply(context,(void *)&reply); // reply for SET -freeReplyObject(reply); -redisGetReply(context,(void *)&reply); // reply for GET -freeReplyObject(reply); -``` -This API can also be used to implement a blocking subscriber: -```c -reply = redisCommand(context,"SUBSCRIBE foo"); -freeReplyObject(reply); -while(redisGetReply(context,(void *)&reply) == REDIS_OK) { - // consume message - freeReplyObject(reply); -} -``` -### Errors - -When a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is -returned. The `err` field inside the context will be non-zero and set to one of the -following constants: - -* **`REDIS_ERR_IO`**: - There was an I/O error while creating the connection, trying to write - to the socket or read from the socket. If you included `errno.h` in your - application, you can use the global `errno` variable to find out what is - wrong. - -* **`REDIS_ERR_EOF`**: - The server closed the connection which resulted in an empty read. - -* **`REDIS_ERR_PROTOCOL`**: - There was an error while parsing the protocol. - -* **`REDIS_ERR_OTHER`**: - Any other error. Currently, it is only used when a specified hostname to connect - to cannot be resolved. - -In every case, the `errstr` field in the context will be set to hold a string representation -of the error. - -## Asynchronous API - -Hiredis comes with an asynchronous API that works easily with any event library. -Examples are bundled that show using Hiredis with [libev](http://software.schmorp.de/pkg/libev.html) -and [libevent](http://monkey.org/~provos/libevent/). - -### Connecting - -The function `redisAsyncConnect` can be used to establish a non-blocking connection to -Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field -should be checked after creation to see if there were errors creating the connection. -Because the connection that will be created is non-blocking, the kernel is not able to -instantly return if the specified host and port is able to accept a connection. - -*Note: A `redisAsyncContext` is not thread-safe.* - -```c -redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); -if (c->err) { - printf("Error: %s\n", c->errstr); - // handle error -} -``` - -The asynchronous context can hold a disconnect callback function that is called when the -connection is disconnected (either because of an error or per user request). This function should -have the following prototype: -```c -void(const redisAsyncContext *c, int status); -``` -On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the -user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err` -field in the context can be accessed to find out the cause of the error. - -The context object is always freed after the disconnect callback fired. When a reconnect is needed, -the disconnect callback is a good point to do so. - -Setting the disconnect callback can only be done once per context. For subsequent calls it will -return `REDIS_ERR`. The function to set the disconnect callback has the following prototype: -```c -int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); -``` -`ac->data` may be used to pass user data to this callback, the same can be done for redisConnectCallback. -### Sending commands and their callbacks - -In an asynchronous context, commands are automatically pipelined due to the nature of an event loop. -Therefore, unlike the synchronous API, there is only a single way to send commands. -Because commands are sent to Redis asynchronously, issuing a command requires a callback function -that is called when the reply is received. Reply callbacks should have the following prototype: -```c -void(redisAsyncContext *c, void *reply, void *privdata); -``` -The `privdata` argument can be used to curry arbitrary data to the callback from the point where -the command is initially queued for execution. - -The functions that can be used to issue commands in an asynchronous context are: -```c -int redisAsyncCommand( - redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, - const char *format, ...); -int redisAsyncCommandArgv( - redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, - int argc, const char **argv, const size_t *argvlen); -``` -Both functions work like their blocking counterparts. The return value is `REDIS_OK` when the command -was successfully added to the output buffer and `REDIS_ERR` otherwise. Example: when the connection -is being disconnected per user-request, no new commands may be added to the output buffer and `REDIS_ERR` is -returned on calls to the `redisAsyncCommand` family. - -If the reply for a command with a `NULL` callback is read, it is immediately freed. When the callback -for a command is non-`NULL`, the memory is freed immediately following the callback: the reply is only -valid for the duration of the callback. - -All pending callbacks are called with a `NULL` reply when the context encountered an error. - -### Disconnecting - -An asynchronous connection can be terminated using: -```c -void redisAsyncDisconnect(redisAsyncContext *ac); -``` -When this function is called, the connection is **not** immediately terminated. Instead, new -commands are no longer accepted and the connection is only terminated when all pending commands -have been written to the socket, their respective replies have been read and their respective -callbacks have been executed. After this, the disconnection callback is executed with the -`REDIS_OK` status and the context object is freed. - -### Hooking it up to event library *X* - -There are a few hooks that need to be set on the context object after it is created. -See the `adapters/` directory for bindings to *libev* and *libevent*. - -## Reply parsing API - -Hiredis comes with a reply parsing API that makes it easy for writing higher -level language bindings. - -The reply parsing API consists of the following functions: -```c -redisReader *redisReaderCreate(void); -void redisReaderFree(redisReader *reader); -int redisReaderFeed(redisReader *reader, const char *buf, size_t len); -int redisReaderGetReply(redisReader *reader, void **reply); -``` -The same set of functions are used internally by hiredis when creating a -normal Redis context, the above API just exposes it to the user for a direct -usage. - -### Usage - -The function `redisReaderCreate` creates a `redisReader` structure that holds a -buffer with unparsed data and state for the protocol parser. - -Incoming data -- most likely from a socket -- can be placed in the internal -buffer of the `redisReader` using `redisReaderFeed`. This function will make a -copy of the buffer pointed to by `buf` for `len` bytes. This data is parsed -when `redisReaderGetReply` is called. This function returns an integer status -and a reply object (as described above) via `void **reply`. The returned status -can be either `REDIS_OK` or `REDIS_ERR`, where the latter means something went -wrong (either a protocol error, or an out of memory error). - -The parser limits the level of nesting for multi bulk payloads to 7. If the -multi bulk nesting level is higher than this, the parser returns an error. - -### Customizing replies - -The function `redisReaderGetReply` creates `redisReply` and makes the function -argument `reply` point to the created `redisReply` variable. For instance, if -the response of type `REDIS_REPLY_STATUS` then the `str` field of `redisReply` -will hold the status as a vanilla C string. However, the functions that are -responsible for creating instances of the `redisReply` can be customized by -setting the `fn` field on the `redisReader` struct. This should be done -immediately after creating the `redisReader`. - -For example, [hiredis-rb](https://github.com/pietern/hiredis-rb/blob/master/ext/hiredis_ext/reader.c) -uses customized reply object functions to create Ruby objects. - -### Reader max buffer - -Both when using the Reader API directly or when using it indirectly via a -normal Redis context, the redisReader structure uses a buffer in order to -accumulate data from the server. -Usually this buffer is destroyed when it is empty and is larger than 16 -KiB in order to avoid wasting memory in unused buffers - -However when working with very big payloads destroying the buffer may slow -down performances considerably, so it is possible to modify the max size of -an idle buffer changing the value of the `maxbuf` field of the reader structure -to the desired value. The special value of 0 means that there is no maximum -value for an idle buffer, so the buffer will never get freed. - -For instance if you have a normal Redis context you can set the maximum idle -buffer to zero (unlimited) just with: -```c -context->reader->maxbuf = 0; -``` -This should be done only in order to maximize performances when working with -large payloads. The context should be set back to `REDIS_READER_MAX_BUF` again -as soon as possible in order to prevent allocation of useless memory. - -### Reader max array elements - -By default the hiredis reply parser sets the maximum number of multi-bulk elements -to 2^32 - 1 or 4,294,967,295 entries. If you need to process multi-bulk replies -with more than this many elements you can set the value higher or to zero, meaning -unlimited with: -```c -context->reader->maxelements = 0; -``` - -## SSL/TLS Support - -### Building - -SSL/TLS support is not built by default and requires an explicit flag: - - make USE_SSL=1 - -This requires OpenSSL development package (e.g. including header files to be -available. - -When enabled, SSL/TLS support is built into extra `libhiredis_ssl.a` and -`libhiredis_ssl.so` static/dynamic libraries. This leaves the original libraries -unaffected so no additional dependencies are introduced. - -### Using it - -First, you'll need to make sure you include the SSL header file: - -```c -#include "hiredis.h" -#include "hiredis_ssl.h" -``` - -You will also need to link against `libhiredis_ssl`, **in addition** to -`libhiredis` and add `-lssl -lcrypto` to satisfy its dependencies. - -Hiredis implements SSL/TLS on top of its normal `redisContext` or -`redisAsyncContext`, so you will need to establish a connection first and then -initiate an SSL/TLS handshake. - -#### Hiredis OpenSSL Wrappers - -Before Hiredis can negotiate an SSL/TLS connection, it is necessary to -initialize OpenSSL and create a context. You can do that in two ways: - -1. Work directly with the OpenSSL API to initialize the library's global context - and create `SSL_CTX *` and `SSL *` contexts. With an `SSL *` object you can - call `redisInitiateSSL()`. -2. Work with a set of Hiredis-provided wrappers around OpenSSL, create a - `redisSSLContext` object to hold configuration and use - `redisInitiateSSLWithContext()` to initiate the SSL/TLS handshake. - -```c -/* An Hiredis SSL context. It holds SSL configuration and can be reused across - * many contexts. - */ -redisSSLContext *ssl; - -/* An error variable to indicate what went wrong, if the context fails to - * initialize. - */ -redisSSLContextError ssl_error; - -/* Initialize global OpenSSL state. - * - * You should call this only once when your app initializes, and only if - * you don't explicitly or implicitly initialize OpenSSL it elsewhere. - */ -redisInitOpenSSL(); - -/* Create SSL context */ -ssl = redisCreateSSLContext( - "cacertbundle.crt", /* File name of trusted CA/ca bundle file, optional */ - "/path/to/certs", /* Path of trusted certificates, optional */ - "client_cert.pem", /* File name of client certificate file, optional */ - "client_key.pem", /* File name of client private key, optional */ - "redis.mydomain.com", /* Server name to request (SNI), optional */ - &ssl_error - ) != REDIS_OK) { - printf("SSL error: %s\n", redisSSLContextGetError(ssl_error); - /* Abort... */ - } - -/* Create Redis context and establish connection */ -c = redisConnect("localhost", 6443); -if (c == NULL || c->err) { - /* Handle error and abort... */ -} - -/* Negotiate SSL/TLS */ -if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) { - /* Handle error, in c->err / c->errstr */ -} -``` - -## RESP3 PUSH replies -Redis 6.0 introduced PUSH replies with the reply-type `>`. These messages are generated spontaneously and can arrive at any time, so must be handled using callbacks. - -### Default behavior -Hiredis installs handlers on `redisContext` and `redisAsyncContext` by default, which will intercept and free any PUSH replies detected. This means existing code will work as-is after upgrading to Redis 6 and switching to `RESP3`. - -### Custom PUSH handler prototypes -The callback prototypes differ between `redisContext` and `redisAsyncContext`. - -#### redisContext -```c -void my_push_handler(void *privdata, void *reply) { - /* Handle the reply */ - - /* Note: We need to free the reply in our custom handler for - blocking contexts. This lets us keep the reply if - we want. */ - freeReplyObject(reply); -} -``` - -#### redisAsyncContext -```c -void my_async_push_handler(redisAsyncContext *ac, void *reply) { - /* Handle the reply */ - - /* Note: Because async hiredis always frees replies, you should - not call freeReplyObject in an async push callback. */ -} -``` - -### Installing a custom handler -There are two ways to set your own PUSH handlers. - -1. Set `push_cb` or `async_push_cb` in the `redisOptions` struct and connect with `redisConnectWithOptions` or `redisAsyncConnectWithOptions`. - ```c - redisOptions = {0}; - REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379); - options->push_cb = my_push_handler; - redisContext *context = redisConnectWithOptions(&options); - ``` -2. Call `redisSetPushCallback` or `redisAsyncSetPushCallback` on a connected context. - ```c - redisContext *context = redisConnect("127.0.0.1", 6379); - redisSetPushCallback(context, my_push_handler); - ``` - - _Note `redisSetPushCallback` and `redisAsyncSetPushCallback` both return any currently configured handler, making it easy to override and then return to the old value._ - -### Specifying no handler -If you have a unique use-case where you don't want hiredis to automatically intercept and free PUSH replies, you will want to configure no handler at all. This can be done in two ways. -1. Set the `REDIS_OPT_NO_PUSH_AUTOFREE` flag in `redisOptions` and leave the callback function pointer `NULL`. - ```c - redisOptions = {0}; - REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379); - options->options |= REDIS_OPT_NO_PUSH_AUTOFREE; - redisContext *context = redisConnectWithOptions(&options); - ``` -3. Call `redisSetPushCallback` with `NULL` once connected. - ```c - redisContext *context = redisConnect("127.0.0.1", 6379); - redisSetPushCallback(context, NULL); - ``` - - _Note: With no handler configured, calls to `redisCommand` may generate more than one reply, so this strategy is only applicable when there's some kind of blocking`redisGetReply()` loop (e.g. `MONITOR` or `SUBSCRIBE` workloads)._ - -## Allocator injection - -Hiredis uses a pass-thru structure of function pointers defined in [alloc.h](https://github.com/redis/hiredis/blob/f5d25850/alloc.h#L41) that contain the currently configured allocation and deallocation functions. By default they just point to libc (`malloc`, `calloc`, `realloc`, etc). - -### Overriding - -One can override the allocators like so: - -```c -hiredisAllocFuncs myfuncs = { - .mallocFn = my_malloc, - .callocFn = my_calloc, - .reallocFn = my_realloc, - .strdupFn = my_strdup, - .freeFn = my_free, -}; - -// Override allocators (function returns current allocators if needed) -hiredisAllocFuncs orig = hiredisSetAllocators(&myfuncs); -``` - -To reset the allocators to their default libc function simply call: - -```c -hiredisResetAllocators(); -``` - -## AUTHORS - -Salvatore Sanfilippo (antirez at gmail),\ -Pieter Noordhuis (pcnoordhuis at gmail)\ -Michael Grunder (michael dot grunder at gmail) - -_Hiredis is released under the BSD license._ diff --git a/ext/hiredis-1.0.2/adapters/ae.h b/ext/hiredis-1.0.2/adapters/ae.h deleted file mode 100644 index 660d82eb0..000000000 --- a/ext/hiredis-1.0.2/adapters/ae.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_AE_H__ -#define __HIREDIS_AE_H__ -#include -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisAeEvents { - redisAsyncContext *context; - aeEventLoop *loop; - int fd; - int reading, writing; -} redisAeEvents; - -static void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) { - ((void)el); ((void)fd); ((void)mask); - - redisAeEvents *e = (redisAeEvents*)privdata; - redisAsyncHandleRead(e->context); -} - -static void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) { - ((void)el); ((void)fd); ((void)mask); - - redisAeEvents *e = (redisAeEvents*)privdata; - redisAsyncHandleWrite(e->context); -} - -static void redisAeAddRead(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (!e->reading) { - e->reading = 1; - aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e); - } -} - -static void redisAeDelRead(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (e->reading) { - e->reading = 0; - aeDeleteFileEvent(loop,e->fd,AE_READABLE); - } -} - -static void redisAeAddWrite(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (!e->writing) { - e->writing = 1; - aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e); - } -} - -static void redisAeDelWrite(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (e->writing) { - e->writing = 0; - aeDeleteFileEvent(loop,e->fd,AE_WRITABLE); - } -} - -static void redisAeCleanup(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - redisAeDelRead(privdata); - redisAeDelWrite(privdata); - hi_free(e); -} - -static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisAeEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisAeEvents*)hi_malloc(sizeof(*e)); - if (e == NULL) - return REDIS_ERR; - - e->context = ac; - e->loop = loop; - e->fd = c->fd; - e->reading = e->writing = 0; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisAeAddRead; - ac->ev.delRead = redisAeDelRead; - ac->ev.addWrite = redisAeAddWrite; - ac->ev.delWrite = redisAeDelWrite; - ac->ev.cleanup = redisAeCleanup; - ac->ev.data = e; - - return REDIS_OK; -} -#endif diff --git a/ext/hiredis-1.0.2/adapters/glib.h b/ext/hiredis-1.0.2/adapters/glib.h deleted file mode 100644 index ad59dd142..000000000 --- a/ext/hiredis-1.0.2/adapters/glib.h +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef __HIREDIS_GLIB_H__ -#define __HIREDIS_GLIB_H__ - -#include - -#include "../hiredis.h" -#include "../async.h" - -typedef struct -{ - GSource source; - redisAsyncContext *ac; - GPollFD poll_fd; -} RedisSource; - -static void -redis_source_add_read (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events |= G_IO_IN; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_del_read (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events &= ~G_IO_IN; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_add_write (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events |= G_IO_OUT; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_del_write (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events &= ~G_IO_OUT; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_cleanup (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - - g_return_if_fail(source); - - redis_source_del_read(source); - redis_source_del_write(source); - /* - * It is not our responsibility to remove ourself from the - * current main loop. However, we will remove the GPollFD. - */ - if (source->poll_fd.fd >= 0) { - g_source_remove_poll((GSource *)data, &source->poll_fd); - source->poll_fd.fd = -1; - } -} - -static gboolean -redis_source_prepare (GSource *source, - gint *timeout_) -{ - RedisSource *redis = (RedisSource *)source; - *timeout_ = -1; - return !!(redis->poll_fd.events & redis->poll_fd.revents); -} - -static gboolean -redis_source_check (GSource *source) -{ - RedisSource *redis = (RedisSource *)source; - return !!(redis->poll_fd.events & redis->poll_fd.revents); -} - -static gboolean -redis_source_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) -{ - RedisSource *redis = (RedisSource *)source; - - if ((redis->poll_fd.revents & G_IO_OUT)) { - redisAsyncHandleWrite(redis->ac); - redis->poll_fd.revents &= ~G_IO_OUT; - } - - if ((redis->poll_fd.revents & G_IO_IN)) { - redisAsyncHandleRead(redis->ac); - redis->poll_fd.revents &= ~G_IO_IN; - } - - if (callback) { - return callback(user_data); - } - - return TRUE; -} - -static void -redis_source_finalize (GSource *source) -{ - RedisSource *redis = (RedisSource *)source; - - if (redis->poll_fd.fd >= 0) { - g_source_remove_poll(source, &redis->poll_fd); - redis->poll_fd.fd = -1; - } -} - -static GSource * -redis_source_new (redisAsyncContext *ac) -{ - static GSourceFuncs source_funcs = { - .prepare = redis_source_prepare, - .check = redis_source_check, - .dispatch = redis_source_dispatch, - .finalize = redis_source_finalize, - }; - redisContext *c = &ac->c; - RedisSource *source; - - g_return_val_if_fail(ac != NULL, NULL); - - source = (RedisSource *)g_source_new(&source_funcs, sizeof *source); - if (source == NULL) - return NULL; - - source->ac = ac; - source->poll_fd.fd = c->fd; - source->poll_fd.events = 0; - source->poll_fd.revents = 0; - g_source_add_poll((GSource *)source, &source->poll_fd); - - ac->ev.addRead = redis_source_add_read; - ac->ev.delRead = redis_source_del_read; - ac->ev.addWrite = redis_source_add_write; - ac->ev.delWrite = redis_source_del_write; - ac->ev.cleanup = redis_source_cleanup; - ac->ev.data = source; - - return (GSource *)source; -} - -#endif /* __HIREDIS_GLIB_H__ */ diff --git a/ext/hiredis-1.0.2/adapters/ivykis.h b/ext/hiredis-1.0.2/adapters/ivykis.h deleted file mode 100644 index 179f6ab52..000000000 --- a/ext/hiredis-1.0.2/adapters/ivykis.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef __HIREDIS_IVYKIS_H__ -#define __HIREDIS_IVYKIS_H__ -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisIvykisEvents { - redisAsyncContext *context; - struct iv_fd fd; -} redisIvykisEvents; - -static void redisIvykisReadEvent(void *arg) { - redisAsyncContext *context = (redisAsyncContext *)arg; - redisAsyncHandleRead(context); -} - -static void redisIvykisWriteEvent(void *arg) { - redisAsyncContext *context = (redisAsyncContext *)arg; - redisAsyncHandleWrite(context); -} - -static void redisIvykisAddRead(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent); -} - -static void redisIvykisDelRead(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_in(&e->fd, NULL); -} - -static void redisIvykisAddWrite(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent); -} - -static void redisIvykisDelWrite(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_out(&e->fd, NULL); -} - -static void redisIvykisCleanup(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - - iv_fd_unregister(&e->fd); - hi_free(e); -} - -static int redisIvykisAttach(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisIvykisEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisIvykisEvents*)hi_malloc(sizeof(*e)); - if (e == NULL) - return REDIS_ERR; - - e->context = ac; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisIvykisAddRead; - ac->ev.delRead = redisIvykisDelRead; - ac->ev.addWrite = redisIvykisAddWrite; - ac->ev.delWrite = redisIvykisDelWrite; - ac->ev.cleanup = redisIvykisCleanup; - ac->ev.data = e; - - /* Initialize and install read/write events */ - IV_FD_INIT(&e->fd); - e->fd.fd = c->fd; - e->fd.handler_in = redisIvykisReadEvent; - e->fd.handler_out = redisIvykisWriteEvent; - e->fd.handler_err = NULL; - e->fd.cookie = e->context; - - iv_fd_register(&e->fd); - - return REDIS_OK; -} -#endif diff --git a/ext/hiredis-1.0.2/adapters/libev.h b/ext/hiredis-1.0.2/adapters/libev.h deleted file mode 100644 index e1e7bbd99..000000000 --- a/ext/hiredis-1.0.2/adapters/libev.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_LIBEV_H__ -#define __HIREDIS_LIBEV_H__ -#include -#include -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisLibevEvents { - redisAsyncContext *context; - struct ev_loop *loop; - int reading, writing; - ev_io rev, wev; - ev_timer timer; -} redisLibevEvents; - -static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) { -#if EV_MULTIPLICITY - ((void)loop); -#endif - ((void)revents); - - redisLibevEvents *e = (redisLibevEvents*)watcher->data; - redisAsyncHandleRead(e->context); -} - -static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) { -#if EV_MULTIPLICITY - ((void)loop); -#endif - ((void)revents); - - redisLibevEvents *e = (redisLibevEvents*)watcher->data; - redisAsyncHandleWrite(e->context); -} - -static void redisLibevAddRead(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (!e->reading) { - e->reading = 1; - ev_io_start(EV_A_ &e->rev); - } -} - -static void redisLibevDelRead(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (e->reading) { - e->reading = 0; - ev_io_stop(EV_A_ &e->rev); - } -} - -static void redisLibevAddWrite(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (!e->writing) { - e->writing = 1; - ev_io_start(EV_A_ &e->wev); - } -} - -static void redisLibevDelWrite(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (e->writing) { - e->writing = 0; - ev_io_stop(EV_A_ &e->wev); - } -} - -static void redisLibevStopTimer(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - ev_timer_stop(EV_A_ &e->timer); -} - -static void redisLibevCleanup(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - redisLibevDelRead(privdata); - redisLibevDelWrite(privdata); - redisLibevStopTimer(privdata); - hi_free(e); -} - -static void redisLibevTimeout(EV_P_ ev_timer *timer, int revents) { - ((void)revents); - redisLibevEvents *e = (redisLibevEvents*)timer->data; - redisAsyncHandleTimeout(e->context); -} - -static void redisLibevSetTimeout(void *privdata, struct timeval tv) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - - if (!ev_is_active(&e->timer)) { - ev_init(&e->timer, redisLibevTimeout); - e->timer.data = e; - } - - e->timer.repeat = tv.tv_sec + tv.tv_usec / 1000000.00; - ev_timer_again(EV_A_ &e->timer); -} - -static int redisLibevAttach(EV_P_ redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisLibevEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisLibevEvents*)hi_calloc(1, sizeof(*e)); - if (e == NULL) - return REDIS_ERR; - - e->context = ac; -#if EV_MULTIPLICITY - e->loop = loop; -#else - e->loop = NULL; -#endif - e->rev.data = e; - e->wev.data = e; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisLibevAddRead; - ac->ev.delRead = redisLibevDelRead; - ac->ev.addWrite = redisLibevAddWrite; - ac->ev.delWrite = redisLibevDelWrite; - ac->ev.cleanup = redisLibevCleanup; - ac->ev.scheduleTimer = redisLibevSetTimeout; - ac->ev.data = e; - - /* Initialize read/write events */ - ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ); - ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE); - return REDIS_OK; -} - -#endif diff --git a/ext/hiredis-1.0.2/adapters/libevent.h b/ext/hiredis-1.0.2/adapters/libevent.h deleted file mode 100644 index 9150979bc..000000000 --- a/ext/hiredis-1.0.2/adapters/libevent.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_LIBEVENT_H__ -#define __HIREDIS_LIBEVENT_H__ -#include -#include "../hiredis.h" -#include "../async.h" - -#define REDIS_LIBEVENT_DELETED 0x01 -#define REDIS_LIBEVENT_ENTERED 0x02 - -typedef struct redisLibeventEvents { - redisAsyncContext *context; - struct event *ev; - struct event_base *base; - struct timeval tv; - short flags; - short state; -} redisLibeventEvents; - -static void redisLibeventDestroy(redisLibeventEvents *e) { - hi_free(e); -} - -static void redisLibeventHandler(int fd, short event, void *arg) { - ((void)fd); - redisLibeventEvents *e = (redisLibeventEvents*)arg; - e->state |= REDIS_LIBEVENT_ENTERED; - - #define CHECK_DELETED() if (e->state & REDIS_LIBEVENT_DELETED) {\ - redisLibeventDestroy(e);\ - return; \ - } - - if ((event & EV_TIMEOUT) && (e->state & REDIS_LIBEVENT_DELETED) == 0) { - redisAsyncHandleTimeout(e->context); - CHECK_DELETED(); - } - - if ((event & EV_READ) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) { - redisAsyncHandleRead(e->context); - CHECK_DELETED(); - } - - if ((event & EV_WRITE) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) { - redisAsyncHandleWrite(e->context); - CHECK_DELETED(); - } - - e->state &= ~REDIS_LIBEVENT_ENTERED; - #undef CHECK_DELETED -} - -static void redisLibeventUpdate(void *privdata, short flag, int isRemove) { - redisLibeventEvents *e = (redisLibeventEvents *)privdata; - const struct timeval *tv = e->tv.tv_sec || e->tv.tv_usec ? &e->tv : NULL; - - if (isRemove) { - if ((e->flags & flag) == 0) { - return; - } else { - e->flags &= ~flag; - } - } else { - if (e->flags & flag) { - return; - } else { - e->flags |= flag; - } - } - - event_del(e->ev); - event_assign(e->ev, e->base, e->context->c.fd, e->flags | EV_PERSIST, - redisLibeventHandler, privdata); - event_add(e->ev, tv); -} - -static void redisLibeventAddRead(void *privdata) { - redisLibeventUpdate(privdata, EV_READ, 0); -} - -static void redisLibeventDelRead(void *privdata) { - redisLibeventUpdate(privdata, EV_READ, 1); -} - -static void redisLibeventAddWrite(void *privdata) { - redisLibeventUpdate(privdata, EV_WRITE, 0); -} - -static void redisLibeventDelWrite(void *privdata) { - redisLibeventUpdate(privdata, EV_WRITE, 1); -} - -static void redisLibeventCleanup(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - if (!e) { - return; - } - event_del(e->ev); - event_free(e->ev); - e->ev = NULL; - - if (e->state & REDIS_LIBEVENT_ENTERED) { - e->state |= REDIS_LIBEVENT_DELETED; - } else { - redisLibeventDestroy(e); - } -} - -static void redisLibeventSetTimeout(void *privdata, struct timeval tv) { - redisLibeventEvents *e = (redisLibeventEvents *)privdata; - short flags = e->flags; - e->flags = 0; - e->tv = tv; - redisLibeventUpdate(e, flags, 0); -} - -static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) { - redisContext *c = &(ac->c); - redisLibeventEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisLibeventEvents*)hi_calloc(1, sizeof(*e)); - if (e == NULL) - return REDIS_ERR; - - e->context = ac; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisLibeventAddRead; - ac->ev.delRead = redisLibeventDelRead; - ac->ev.addWrite = redisLibeventAddWrite; - ac->ev.delWrite = redisLibeventDelWrite; - ac->ev.cleanup = redisLibeventCleanup; - ac->ev.scheduleTimer = redisLibeventSetTimeout; - ac->ev.data = e; - - /* Initialize and install read/write events */ - e->ev = event_new(base, c->fd, EV_READ | EV_WRITE, redisLibeventHandler, e); - e->base = base; - return REDIS_OK; -} -#endif diff --git a/ext/hiredis-1.0.2/adapters/libuv.h b/ext/hiredis-1.0.2/adapters/libuv.h deleted file mode 100644 index c120b1b39..000000000 --- a/ext/hiredis-1.0.2/adapters/libuv.h +++ /dev/null @@ -1,117 +0,0 @@ -#ifndef __HIREDIS_LIBUV_H__ -#define __HIREDIS_LIBUV_H__ -#include -#include -#include "../hiredis.h" -#include "../async.h" -#include - -typedef struct redisLibuvEvents { - redisAsyncContext* context; - uv_poll_t handle; - int events; -} redisLibuvEvents; - - -static void redisLibuvPoll(uv_poll_t* handle, int status, int events) { - redisLibuvEvents* p = (redisLibuvEvents*)handle->data; - int ev = (status ? p->events : events); - - if (p->context != NULL && (ev & UV_READABLE)) { - redisAsyncHandleRead(p->context); - } - if (p->context != NULL && (ev & UV_WRITABLE)) { - redisAsyncHandleWrite(p->context); - } -} - - -static void redisLibuvAddRead(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events |= UV_READABLE; - - uv_poll_start(&p->handle, p->events, redisLibuvPoll); -} - - -static void redisLibuvDelRead(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events &= ~UV_READABLE; - - if (p->events) { - uv_poll_start(&p->handle, p->events, redisLibuvPoll); - } else { - uv_poll_stop(&p->handle); - } -} - - -static void redisLibuvAddWrite(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events |= UV_WRITABLE; - - uv_poll_start(&p->handle, p->events, redisLibuvPoll); -} - - -static void redisLibuvDelWrite(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events &= ~UV_WRITABLE; - - if (p->events) { - uv_poll_start(&p->handle, p->events, redisLibuvPoll); - } else { - uv_poll_stop(&p->handle); - } -} - - -static void on_close(uv_handle_t* handle) { - redisLibuvEvents* p = (redisLibuvEvents*)handle->data; - - hi_free(p); -} - - -static void redisLibuvCleanup(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->context = NULL; // indicate that context might no longer exist - uv_close((uv_handle_t*)&p->handle, on_close); -} - - -static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) { - redisContext *c = &(ac->c); - - if (ac->ev.data != NULL) { - return REDIS_ERR; - } - - ac->ev.addRead = redisLibuvAddRead; - ac->ev.delRead = redisLibuvDelRead; - ac->ev.addWrite = redisLibuvAddWrite; - ac->ev.delWrite = redisLibuvDelWrite; - ac->ev.cleanup = redisLibuvCleanup; - - redisLibuvEvents* p = (redisLibuvEvents*)hi_malloc(sizeof(*p)); - if (p == NULL) - return REDIS_ERR; - - memset(p, 0, sizeof(*p)); - - if (uv_poll_init_socket(loop, &p->handle, c->fd) != 0) { - return REDIS_ERR; - } - - ac->ev.data = p; - p->handle.data = p; - p->context = ac; - - return REDIS_OK; -} -#endif diff --git a/ext/hiredis-1.0.2/adapters/macosx.h b/ext/hiredis-1.0.2/adapters/macosx.h deleted file mode 100644 index 3c87f1b2f..000000000 --- a/ext/hiredis-1.0.2/adapters/macosx.h +++ /dev/null @@ -1,115 +0,0 @@ -// -// Created by Дмитрий Бахвалов on 13.07.15. -// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved. -// - -#ifndef __HIREDIS_MACOSX_H__ -#define __HIREDIS_MACOSX_H__ - -#include - -#include "../hiredis.h" -#include "../async.h" - -typedef struct { - redisAsyncContext *context; - CFSocketRef socketRef; - CFRunLoopSourceRef sourceRef; -} RedisRunLoop; - -static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) { - if( redisRunLoop != NULL ) { - if( redisRunLoop->sourceRef != NULL ) { - CFRunLoopSourceInvalidate(redisRunLoop->sourceRef); - CFRelease(redisRunLoop->sourceRef); - } - if( redisRunLoop->socketRef != NULL ) { - CFSocketInvalidate(redisRunLoop->socketRef); - CFRelease(redisRunLoop->socketRef); - } - hi_free(redisRunLoop); - } - return REDIS_ERR; -} - -static void redisMacOSAddRead(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack); -} - -static void redisMacOSDelRead(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack); -} - -static void redisMacOSAddWrite(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack); -} - -static void redisMacOSDelWrite(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack); -} - -static void redisMacOSCleanup(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - freeRedisRunLoop(redisRunLoop); -} - -static void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) { - redisAsyncContext* context = (redisAsyncContext*) info; - - switch (callbackType) { - case kCFSocketReadCallBack: - redisAsyncHandleRead(context); - break; - - case kCFSocketWriteCallBack: - redisAsyncHandleWrite(context); - break; - - default: - break; - } -} - -static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) { - redisContext *redisCtx = &(redisAsyncCtx->c); - - /* Nothing should be attached when something is already attached */ - if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR; - - RedisRunLoop* redisRunLoop = (RedisRunLoop*) hi_calloc(1, sizeof(RedisRunLoop)); - if (redisRunLoop == NULL) - return REDIS_ERR; - - /* Setup redis stuff */ - redisRunLoop->context = redisAsyncCtx; - - redisAsyncCtx->ev.addRead = redisMacOSAddRead; - redisAsyncCtx->ev.delRead = redisMacOSDelRead; - redisAsyncCtx->ev.addWrite = redisMacOSAddWrite; - redisAsyncCtx->ev.delWrite = redisMacOSDelWrite; - redisAsyncCtx->ev.cleanup = redisMacOSCleanup; - redisAsyncCtx->ev.data = redisRunLoop; - - /* Initialize and install read/write events */ - CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL }; - - redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd, - kCFSocketReadCallBack | kCFSocketWriteCallBack, - redisMacOSAsyncCallback, - &socketCtx); - if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop); - - redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0); - if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop); - - CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode); - - return REDIS_OK; -} - -#endif - diff --git a/ext/hiredis-1.0.2/adapters/qt.h b/ext/hiredis-1.0.2/adapters/qt.h deleted file mode 100644 index 5cc02e6ce..000000000 --- a/ext/hiredis-1.0.2/adapters/qt.h +++ /dev/null @@ -1,135 +0,0 @@ -/*- - * Copyright (C) 2014 Pietro Cerutti - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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. - */ - -#ifndef __HIREDIS_QT_H__ -#define __HIREDIS_QT_H__ -#include -#include "../async.h" - -static void RedisQtAddRead(void *); -static void RedisQtDelRead(void *); -static void RedisQtAddWrite(void *); -static void RedisQtDelWrite(void *); -static void RedisQtCleanup(void *); - -class RedisQtAdapter : public QObject { - - Q_OBJECT - - friend - void RedisQtAddRead(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->addRead(); - } - - friend - void RedisQtDelRead(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->delRead(); - } - - friend - void RedisQtAddWrite(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->addWrite(); - } - - friend - void RedisQtDelWrite(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->delWrite(); - } - - friend - void RedisQtCleanup(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->cleanup(); - } - - public: - RedisQtAdapter(QObject * parent = 0) - : QObject(parent), m_ctx(0), m_read(0), m_write(0) { } - - ~RedisQtAdapter() { - if (m_ctx != 0) { - m_ctx->ev.data = NULL; - } - } - - int setContext(redisAsyncContext * ac) { - if (ac->ev.data != NULL) { - return REDIS_ERR; - } - m_ctx = ac; - m_ctx->ev.data = this; - m_ctx->ev.addRead = RedisQtAddRead; - m_ctx->ev.delRead = RedisQtDelRead; - m_ctx->ev.addWrite = RedisQtAddWrite; - m_ctx->ev.delWrite = RedisQtDelWrite; - m_ctx->ev.cleanup = RedisQtCleanup; - return REDIS_OK; - } - - private: - void addRead() { - if (m_read) return; - m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0); - connect(m_read, SIGNAL(activated(int)), this, SLOT(read())); - } - - void delRead() { - if (!m_read) return; - delete m_read; - m_read = 0; - } - - void addWrite() { - if (m_write) return; - m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0); - connect(m_write, SIGNAL(activated(int)), this, SLOT(write())); - } - - void delWrite() { - if (!m_write) return; - delete m_write; - m_write = 0; - } - - void cleanup() { - delRead(); - delWrite(); - } - - private slots: - void read() { redisAsyncHandleRead(m_ctx); } - void write() { redisAsyncHandleWrite(m_ctx); } - - private: - redisAsyncContext * m_ctx; - QSocketNotifier * m_read; - QSocketNotifier * m_write; -}; - -#endif /* !__HIREDIS_QT_H__ */ diff --git a/ext/hiredis-1.0.2/alloc.c b/ext/hiredis-1.0.2/alloc.c deleted file mode 100644 index 7fb6b35e7..000000000 --- a/ext/hiredis-1.0.2/alloc.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2020, Michael Grunder - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "fmacros.h" -#include "alloc.h" -#include -#include - -hiredisAllocFuncs hiredisAllocFns = { - .mallocFn = malloc, - .callocFn = calloc, - .reallocFn = realloc, - .strdupFn = strdup, - .freeFn = free, -}; - -/* Override hiredis' allocators with ones supplied by the user */ -hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *override) { - hiredisAllocFuncs orig = hiredisAllocFns; - - hiredisAllocFns = *override; - - return orig; -} - -/* Reset allocators to use libc defaults */ -void hiredisResetAllocators(void) { - hiredisAllocFns = (hiredisAllocFuncs) { - .mallocFn = malloc, - .callocFn = calloc, - .reallocFn = realloc, - .strdupFn = strdup, - .freeFn = free, - }; -} - -#ifdef _WIN32 - -void *hi_malloc(size_t size) { - return hiredisAllocFns.mallocFn(size); -} - -void *hi_calloc(size_t nmemb, size_t size) { - return hiredisAllocFns.callocFn(nmemb, size); -} - -void *hi_realloc(void *ptr, size_t size) { - return hiredisAllocFns.reallocFn(ptr, size); -} - -char *hi_strdup(const char *str) { - return hiredisAllocFns.strdupFn(str); -} - -void hi_free(void *ptr) { - hiredisAllocFns.freeFn(ptr); -} - -#endif diff --git a/ext/hiredis-1.0.2/alloc.h b/ext/hiredis-1.0.2/alloc.h deleted file mode 100644 index 34a05f49f..000000000 --- a/ext/hiredis-1.0.2/alloc.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2020, Michael Grunder - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef HIREDIS_ALLOC_H -#define HIREDIS_ALLOC_H - -#include /* for size_t */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* Structure pointing to our actually configured allocators */ -typedef struct hiredisAllocFuncs { - void *(*mallocFn)(size_t); - void *(*callocFn)(size_t,size_t); - void *(*reallocFn)(void*,size_t); - char *(*strdupFn)(const char*); - void (*freeFn)(void*); -} hiredisAllocFuncs; - -hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha); -void hiredisResetAllocators(void); - -#ifndef _WIN32 - -/* Hiredis' configured allocator function pointer struct */ -extern hiredisAllocFuncs hiredisAllocFns; - -static inline void *hi_malloc(size_t size) { - return hiredisAllocFns.mallocFn(size); -} - -static inline void *hi_calloc(size_t nmemb, size_t size) { - return hiredisAllocFns.callocFn(nmemb, size); -} - -static inline void *hi_realloc(void *ptr, size_t size) { - return hiredisAllocFns.reallocFn(ptr, size); -} - -static inline char *hi_strdup(const char *str) { - return hiredisAllocFns.strdupFn(str); -} - -static inline void hi_free(void *ptr) { - hiredisAllocFns.freeFn(ptr); -} - -#else - -void *hi_malloc(size_t size); -void *hi_calloc(size_t nmemb, size_t size); -void *hi_realloc(void *ptr, size_t size); -char *hi_strdup(const char *str); -void hi_free(void *ptr); - -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* HIREDIS_ALLOC_H */ diff --git a/ext/hiredis-1.0.2/appveyor.yml b/ext/hiredis-1.0.2/appveyor.yml deleted file mode 100644 index 5b43fdbeb..000000000 --- a/ext/hiredis-1.0.2/appveyor.yml +++ /dev/null @@ -1,24 +0,0 @@ -# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin) -environment: - matrix: - - CYG_BASH: C:\cygwin64\bin\bash - CC: gcc - - CYG_BASH: C:\cygwin\bin\bash - CC: gcc - CFLAGS: -m32 - CXXFLAGS: -m32 - LDFLAGS: -m32 - -clone_depth: 1 - -# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail -init: - - git config --global core.autocrlf input - -# Install needed build dependencies -install: - - '%CYG_BASH% -lc "cygcheck -dc cygwin"' - -build_script: - - 'echo building...' - - '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0 - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "fmacros.h" -#include "alloc.h" -#include -#include -#ifndef _MSC_VER -#include -#endif -#include -#include -#include -#include "async.h" -#include "net.h" -#include "dict.c" -#include "sds.h" -#include "win32.h" - -#include "async_private.h" - -/* Forward declarations of hiredis.c functions */ -int __redisAppendCommand(redisContext *c, const char *cmd, size_t len); -void __redisSetError(redisContext *c, int type, const char *str); - -/* Functions managing dictionary of callbacks for pub/sub. */ -static unsigned int callbackHash(const void *key) { - return dictGenHashFunction((const unsigned char *)key, - sdslen((const sds)key)); -} - -static void *callbackValDup(void *privdata, const void *src) { - ((void) privdata); - redisCallback *dup; - - dup = hi_malloc(sizeof(*dup)); - if (dup == NULL) - return NULL; - - memcpy(dup,src,sizeof(*dup)); - return dup; -} - -static int callbackKeyCompare(void *privdata, const void *key1, const void *key2) { - int l1, l2; - ((void) privdata); - - l1 = sdslen((const sds)key1); - l2 = sdslen((const sds)key2); - if (l1 != l2) return 0; - return memcmp(key1,key2,l1) == 0; -} - -static void callbackKeyDestructor(void *privdata, void *key) { - ((void) privdata); - sdsfree((sds)key); -} - -static void callbackValDestructor(void *privdata, void *val) { - ((void) privdata); - hi_free(val); -} - -static dictType callbackDict = { - callbackHash, - NULL, - callbackValDup, - callbackKeyCompare, - callbackKeyDestructor, - callbackValDestructor -}; - -static redisAsyncContext *redisAsyncInitialize(redisContext *c) { - redisAsyncContext *ac; - dict *channels = NULL, *patterns = NULL; - - channels = dictCreate(&callbackDict,NULL); - if (channels == NULL) - goto oom; - - patterns = dictCreate(&callbackDict,NULL); - if (patterns == NULL) - goto oom; - - ac = hi_realloc(c,sizeof(redisAsyncContext)); - if (ac == NULL) - goto oom; - - c = &(ac->c); - - /* The regular connect functions will always set the flag REDIS_CONNECTED. - * For the async API, we want to wait until the first write event is - * received up before setting this flag, so reset it here. */ - c->flags &= ~REDIS_CONNECTED; - - ac->err = 0; - ac->errstr = NULL; - ac->data = NULL; - ac->dataCleanup = NULL; - - ac->ev.data = NULL; - ac->ev.addRead = NULL; - ac->ev.delRead = NULL; - ac->ev.addWrite = NULL; - ac->ev.delWrite = NULL; - ac->ev.cleanup = NULL; - ac->ev.scheduleTimer = NULL; - - ac->onConnect = NULL; - ac->onDisconnect = NULL; - - ac->replies.head = NULL; - ac->replies.tail = NULL; - ac->sub.invalid.head = NULL; - ac->sub.invalid.tail = NULL; - ac->sub.channels = channels; - ac->sub.patterns = patterns; - - return ac; -oom: - if (channels) dictRelease(channels); - if (patterns) dictRelease(patterns); - return NULL; -} - -/* We want the error field to be accessible directly instead of requiring - * an indirection to the redisContext struct. */ -static void __redisAsyncCopyError(redisAsyncContext *ac) { - if (!ac) - return; - - redisContext *c = &(ac->c); - ac->err = c->err; - ac->errstr = c->errstr; -} - -redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options) { - redisOptions myOptions = *options; - redisContext *c; - redisAsyncContext *ac; - - /* Clear any erroneously set sync callback and flag that we don't want to - * use freeReplyObject by default. */ - myOptions.push_cb = NULL; - myOptions.options |= REDIS_OPT_NO_PUSH_AUTOFREE; - - myOptions.options |= REDIS_OPT_NONBLOCK; - c = redisConnectWithOptions(&myOptions); - if (c == NULL) { - return NULL; - } - - ac = redisAsyncInitialize(c); - if (ac == NULL) { - redisFree(c); - return NULL; - } - - /* Set any configured async push handler */ - redisAsyncSetPushCallback(ac, myOptions.async_push_cb); - - __redisAsyncCopyError(ac); - return ac; -} - -redisAsyncContext *redisAsyncConnect(const char *ip, int port) { - redisOptions options = {0}; - REDIS_OPTIONS_SET_TCP(&options, ip, port); - return redisAsyncConnectWithOptions(&options); -} - -redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, - const char *source_addr) { - redisOptions options = {0}; - REDIS_OPTIONS_SET_TCP(&options, ip, port); - options.endpoint.tcp.source_addr = source_addr; - return redisAsyncConnectWithOptions(&options); -} - -redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, - const char *source_addr) { - redisOptions options = {0}; - REDIS_OPTIONS_SET_TCP(&options, ip, port); - options.options |= REDIS_OPT_REUSEADDR; - options.endpoint.tcp.source_addr = source_addr; - return redisAsyncConnectWithOptions(&options); -} - -redisAsyncContext *redisAsyncConnectUnix(const char *path) { - redisOptions options = {0}; - REDIS_OPTIONS_SET_UNIX(&options, path); - return redisAsyncConnectWithOptions(&options); -} - -int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) { - if (ac->onConnect == NULL) { - ac->onConnect = fn; - - /* The common way to detect an established connection is to wait for - * the first write event to be fired. This assumes the related event - * library functions are already set. */ - _EL_ADD_WRITE(ac); - return REDIS_OK; - } - return REDIS_ERR; -} - -int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) { - if (ac->onDisconnect == NULL) { - ac->onDisconnect = fn; - return REDIS_OK; - } - return REDIS_ERR; -} - -/* Helper functions to push/shift callbacks */ -static int __redisPushCallback(redisCallbackList *list, redisCallback *source) { - redisCallback *cb; - - /* Copy callback from stack to heap */ - cb = hi_malloc(sizeof(*cb)); - if (cb == NULL) - return REDIS_ERR_OOM; - - if (source != NULL) { - memcpy(cb,source,sizeof(*cb)); - cb->next = NULL; - } - - /* Store callback in list */ - if (list->head == NULL) - list->head = cb; - if (list->tail != NULL) - list->tail->next = cb; - list->tail = cb; - return REDIS_OK; -} - -static int __redisShiftCallback(redisCallbackList *list, redisCallback *target) { - redisCallback *cb = list->head; - if (cb != NULL) { - list->head = cb->next; - if (cb == list->tail) - list->tail = NULL; - - /* Copy callback from heap to stack */ - if (target != NULL) - memcpy(target,cb,sizeof(*cb)); - hi_free(cb); - return REDIS_OK; - } - return REDIS_ERR; -} - -static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) { - redisContext *c = &(ac->c); - if (cb->fn != NULL) { - c->flags |= REDIS_IN_CALLBACK; - cb->fn(ac,reply,cb->privdata); - c->flags &= ~REDIS_IN_CALLBACK; - } -} - -static void __redisRunPushCallback(redisAsyncContext *ac, redisReply *reply) { - if (ac->push_cb != NULL) { - ac->c.flags |= REDIS_IN_CALLBACK; - ac->push_cb(ac, reply); - ac->c.flags &= ~REDIS_IN_CALLBACK; - } -} - -/* Helper function to free the context. */ -static void __redisAsyncFree(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisCallback cb; - dictIterator *it; - dictEntry *de; - - /* Execute pending callbacks with NULL reply. */ - while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK) - __redisRunCallback(ac,&cb,NULL); - - /* Execute callbacks for invalid commands */ - while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK) - __redisRunCallback(ac,&cb,NULL); - - /* Run subscription callbacks with NULL reply */ - if (ac->sub.channels) { - it = dictGetIterator(ac->sub.channels); - if (it != NULL) { - while ((de = dictNext(it)) != NULL) - __redisRunCallback(ac,dictGetEntryVal(de),NULL); - dictReleaseIterator(it); - } - - dictRelease(ac->sub.channels); - } - - if (ac->sub.patterns) { - it = dictGetIterator(ac->sub.patterns); - if (it != NULL) { - while ((de = dictNext(it)) != NULL) - __redisRunCallback(ac,dictGetEntryVal(de),NULL); - dictReleaseIterator(it); - } - - dictRelease(ac->sub.patterns); - } - - /* Signal event lib to clean up */ - _EL_CLEANUP(ac); - - /* Execute disconnect callback. When redisAsyncFree() initiated destroying - * this context, the status will always be REDIS_OK. */ - if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) { - if (c->flags & REDIS_FREEING) { - ac->onDisconnect(ac,REDIS_OK); - } else { - ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR); - } - } - - if (ac->dataCleanup) { - ac->dataCleanup(ac->data); - } - - /* Cleanup self */ - redisFree(c); -} - -/* Free the async context. When this function is called from a callback, - * control needs to be returned to redisProcessCallbacks() before actual - * free'ing. To do so, a flag is set on the context which is picked up by - * redisProcessCallbacks(). Otherwise, the context is immediately free'd. */ -void redisAsyncFree(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - c->flags |= REDIS_FREEING; - if (!(c->flags & REDIS_IN_CALLBACK)) - __redisAsyncFree(ac); -} - -/* Helper function to make the disconnect happen and clean up. */ -void __redisAsyncDisconnect(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - - /* Make sure error is accessible if there is any */ - __redisAsyncCopyError(ac); - - if (ac->err == 0) { - /* For clean disconnects, there should be no pending callbacks. */ - int ret = __redisShiftCallback(&ac->replies,NULL); - assert(ret == REDIS_ERR); - } else { - /* Disconnection is caused by an error, make sure that pending - * callbacks cannot call new commands. */ - c->flags |= REDIS_DISCONNECTING; - } - - /* cleanup event library on disconnect. - * this is safe to call multiple times */ - _EL_CLEANUP(ac); - - /* For non-clean disconnects, __redisAsyncFree() will execute pending - * callbacks with a NULL-reply. */ - if (!(c->flags & REDIS_NO_AUTO_FREE)) { - __redisAsyncFree(ac); - } -} - -/* Tries to do a clean disconnect from Redis, meaning it stops new commands - * from being issued, but tries to flush the output buffer and execute - * callbacks for all remaining replies. When this function is called from a - * callback, there might be more replies and we can safely defer disconnecting - * to redisProcessCallbacks(). Otherwise, we can only disconnect immediately - * when there are no pending callbacks. */ -void redisAsyncDisconnect(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - c->flags |= REDIS_DISCONNECTING; - - /** unset the auto-free flag here, because disconnect undoes this */ - c->flags &= ~REDIS_NO_AUTO_FREE; - if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL) - __redisAsyncDisconnect(ac); -} - -static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) { - redisContext *c = &(ac->c); - dict *callbacks; - redisCallback *cb; - dictEntry *de; - int pvariant; - char *stype; - sds sname; - - /* Custom reply functions are not supported for pub/sub. This will fail - * very hard when they are used... */ - if (reply->type == REDIS_REPLY_ARRAY || reply->type == REDIS_REPLY_PUSH) { - assert(reply->elements >= 2); - assert(reply->element[0]->type == REDIS_REPLY_STRING); - stype = reply->element[0]->str; - pvariant = (tolower(stype[0]) == 'p') ? 1 : 0; - - if (pvariant) - callbacks = ac->sub.patterns; - else - callbacks = ac->sub.channels; - - /* Locate the right callback */ - assert(reply->element[1]->type == REDIS_REPLY_STRING); - sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len); - if (sname == NULL) - goto oom; - - de = dictFind(callbacks,sname); - if (de != NULL) { - cb = dictGetEntryVal(de); - - /* If this is an subscribe reply decrease pending counter. */ - if (strcasecmp(stype+pvariant,"subscribe") == 0) { - cb->pending_subs -= 1; - } - - memcpy(dstcb,cb,sizeof(*dstcb)); - - /* If this is an unsubscribe message, remove it. */ - if (strcasecmp(stype+pvariant,"unsubscribe") == 0) { - if (cb->pending_subs == 0) - dictDelete(callbacks,sname); - - /* If this was the last unsubscribe message, revert to - * non-subscribe mode. */ - assert(reply->element[2]->type == REDIS_REPLY_INTEGER); - - /* Unset subscribed flag only when no pipelined pending subscribe. */ - if (reply->element[2]->integer == 0 - && dictSize(ac->sub.channels) == 0 - && dictSize(ac->sub.patterns) == 0) - c->flags &= ~REDIS_SUBSCRIBED; - } - } - sdsfree(sname); - } else { - /* Shift callback for invalid commands. */ - __redisShiftCallback(&ac->sub.invalid,dstcb); - } - return REDIS_OK; -oom: - __redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory"); - return REDIS_ERR; -} - -#define redisIsSpontaneousPushReply(r) \ - (redisIsPushReply(r) && !redisIsSubscribeReply(r)) - -static int redisIsSubscribeReply(redisReply *reply) { - char *str; - size_t len, off; - - /* We will always have at least one string with the subscribe/message type */ - if (reply->elements < 1 || reply->element[0]->type != REDIS_REPLY_STRING || - reply->element[0]->len < sizeof("message") - 1) - { - return 0; - } - - /* Get the string/len moving past 'p' if needed */ - off = tolower(reply->element[0]->str[0]) == 'p'; - str = reply->element[0]->str + off; - len = reply->element[0]->len - off; - - return !strncasecmp(str, "subscribe", len) || - !strncasecmp(str, "message", len); - -} - -void redisProcessCallbacks(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisCallback cb = {NULL, NULL, 0, NULL}; - void *reply = NULL; - int status; - - while((status = redisGetReply(c,&reply)) == REDIS_OK) { - if (reply == NULL) { - /* When the connection is being disconnected and there are - * no more replies, this is the cue to really disconnect. */ - if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0 - && ac->replies.head == NULL) { - __redisAsyncDisconnect(ac); - return; - } - - /* If monitor mode, repush callback */ - if(c->flags & REDIS_MONITORING) { - __redisPushCallback(&ac->replies,&cb); - } - - /* When the connection is not being disconnected, simply stop - * trying to get replies and wait for the next loop tick. */ - break; - } - - /* Send any non-subscribe related PUSH messages to our PUSH handler - * while allowing subscribe related PUSH messages to pass through. - * This allows existing code to be backward compatible and work in - * either RESP2 or RESP3 mode. */ - if (redisIsSpontaneousPushReply(reply)) { - __redisRunPushCallback(ac, reply); - c->reader->fn->freeObject(reply); - continue; - } - - /* Even if the context is subscribed, pending regular - * callbacks will get a reply before pub/sub messages arrive. */ - if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) { - /* - * A spontaneous reply in a not-subscribed context can be the error - * reply that is sent when a new connection exceeds the maximum - * number of allowed connections on the server side. - * - * This is seen as an error instead of a regular reply because the - * server closes the connection after sending it. - * - * To prevent the error from being overwritten by an EOF error the - * connection is closed here. See issue #43. - * - * Another possibility is that the server is loading its dataset. - * In this case we also want to close the connection, and have the - * user wait until the server is ready to take our request. - */ - if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) { - c->err = REDIS_ERR_OTHER; - snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str); - c->reader->fn->freeObject(reply); - __redisAsyncDisconnect(ac); - return; - } - /* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */ - assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING)); - if(c->flags & REDIS_SUBSCRIBED) - __redisGetSubscribeCallback(ac,reply,&cb); - } - - if (cb.fn != NULL) { - __redisRunCallback(ac,&cb,reply); - c->reader->fn->freeObject(reply); - - /* Proceed with free'ing when redisAsyncFree() was called. */ - if (c->flags & REDIS_FREEING) { - __redisAsyncFree(ac); - return; - } - } else { - /* No callback for this reply. This can either be a NULL callback, - * or there were no callbacks to begin with. Either way, don't - * abort with an error, but simply ignore it because the client - * doesn't know what the server will spit out over the wire. */ - c->reader->fn->freeObject(reply); - } - } - - /* Disconnect when there was an error reading the reply */ - if (status != REDIS_OK) - __redisAsyncDisconnect(ac); -} - -static void __redisAsyncHandleConnectFailure(redisAsyncContext *ac) { - if (ac->onConnect) ac->onConnect(ac, REDIS_ERR); - __redisAsyncDisconnect(ac); -} - -/* Internal helper function to detect socket status the first time a read or - * write event fires. When connecting was not successful, the connect callback - * is called with a REDIS_ERR status and the context is free'd. */ -static int __redisAsyncHandleConnect(redisAsyncContext *ac) { - int completed = 0; - redisContext *c = &(ac->c); - - if (redisCheckConnectDone(c, &completed) == REDIS_ERR) { - /* Error! */ - redisCheckSocketError(c); - __redisAsyncHandleConnectFailure(ac); - return REDIS_ERR; - } else if (completed == 1) { - /* connected! */ - if (c->connection_type == REDIS_CONN_TCP && - redisSetTcpNoDelay(c) == REDIS_ERR) { - __redisAsyncHandleConnectFailure(ac); - return REDIS_ERR; - } - - if (ac->onConnect) ac->onConnect(ac, REDIS_OK); - c->flags |= REDIS_CONNECTED; - return REDIS_OK; - } else { - return REDIS_OK; - } -} - -void redisAsyncRead(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - - if (redisBufferRead(c) == REDIS_ERR) { - __redisAsyncDisconnect(ac); - } else { - /* Always re-schedule reads */ - _EL_ADD_READ(ac); - redisProcessCallbacks(ac); - } -} - -/* This function should be called when the socket is readable. - * It processes all replies that can be read and executes their callbacks. - */ -void redisAsyncHandleRead(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - - if (!(c->flags & REDIS_CONNECTED)) { - /* Abort connect was not successful. */ - if (__redisAsyncHandleConnect(ac) != REDIS_OK) - return; - /* Try again later when the context is still not connected. */ - if (!(c->flags & REDIS_CONNECTED)) - return; - } - - c->funcs->async_read(ac); -} - -void redisAsyncWrite(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - int done = 0; - - if (redisBufferWrite(c,&done) == REDIS_ERR) { - __redisAsyncDisconnect(ac); - } else { - /* Continue writing when not done, stop writing otherwise */ - if (!done) - _EL_ADD_WRITE(ac); - else - _EL_DEL_WRITE(ac); - - /* Always schedule reads after writes */ - _EL_ADD_READ(ac); - } -} - -void redisAsyncHandleWrite(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - - if (!(c->flags & REDIS_CONNECTED)) { - /* Abort connect was not successful. */ - if (__redisAsyncHandleConnect(ac) != REDIS_OK) - return; - /* Try again later when the context is still not connected. */ - if (!(c->flags & REDIS_CONNECTED)) - return; - } - - c->funcs->async_write(ac); -} - -void redisAsyncHandleTimeout(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisCallback cb; - - if ((c->flags & REDIS_CONNECTED) && ac->replies.head == NULL) { - /* Nothing to do - just an idle timeout */ - return; - } - - if (!c->err) { - __redisSetError(c, REDIS_ERR_TIMEOUT, "Timeout"); - } - - if (!(c->flags & REDIS_CONNECTED) && ac->onConnect) { - ac->onConnect(ac, REDIS_ERR); - } - - while (__redisShiftCallback(&ac->replies, &cb) == REDIS_OK) { - __redisRunCallback(ac, &cb, NULL); - } - - /** - * TODO: Don't automatically sever the connection, - * rather, allow to ignore responses before the queue is clear - */ - __redisAsyncDisconnect(ac); -} - -/* Sets a pointer to the first argument and its length starting at p. Returns - * the number of bytes to skip to get to the following argument. */ -static const char *nextArgument(const char *start, const char **str, size_t *len) { - const char *p = start; - if (p[0] != '$') { - p = strchr(p,'$'); - if (p == NULL) return NULL; - } - - *len = (int)strtol(p+1,NULL,10); - p = strchr(p,'\r'); - assert(p); - *str = p+2; - return p+2+(*len)+2; -} - -/* Helper function for the redisAsyncCommand* family of functions. Writes a - * formatted command to the output buffer and registers the provided callback - * function with the context. */ -static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) { - redisContext *c = &(ac->c); - redisCallback cb; - struct dict *cbdict; - dictEntry *de; - redisCallback *existcb; - int pvariant, hasnext; - const char *cstr, *astr; - size_t clen, alen; - const char *p; - sds sname; - int ret; - - /* Don't accept new commands when the connection is about to be closed. */ - if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR; - - /* Setup callback */ - cb.fn = fn; - cb.privdata = privdata; - cb.pending_subs = 1; - - /* Find out which command will be appended. */ - p = nextArgument(cmd,&cstr,&clen); - assert(p != NULL); - hasnext = (p[0] == '$'); - pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0; - cstr += pvariant; - clen -= pvariant; - - if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) { - c->flags |= REDIS_SUBSCRIBED; - - /* Add every channel/pattern to the list of subscription callbacks. */ - while ((p = nextArgument(p,&astr,&alen)) != NULL) { - sname = sdsnewlen(astr,alen); - if (sname == NULL) - goto oom; - - if (pvariant) - cbdict = ac->sub.patterns; - else - cbdict = ac->sub.channels; - - de = dictFind(cbdict,sname); - - if (de != NULL) { - existcb = dictGetEntryVal(de); - cb.pending_subs = existcb->pending_subs + 1; - } - - ret = dictReplace(cbdict,sname,&cb); - - if (ret == 0) sdsfree(sname); - } - } else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) { - /* It is only useful to call (P)UNSUBSCRIBE when the context is - * subscribed to one or more channels or patterns. */ - if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR; - - /* (P)UNSUBSCRIBE does not have its own response: every channel or - * pattern that is unsubscribed will receive a message. This means we - * should not append a callback function for this command. */ - } else if(strncasecmp(cstr,"monitor\r\n",9) == 0) { - /* Set monitor flag and push callback */ - c->flags |= REDIS_MONITORING; - __redisPushCallback(&ac->replies,&cb); - } else { - if (c->flags & REDIS_SUBSCRIBED) - /* This will likely result in an error reply, but it needs to be - * received and passed to the callback. */ - __redisPushCallback(&ac->sub.invalid,&cb); - else - __redisPushCallback(&ac->replies,&cb); - } - - __redisAppendCommand(c,cmd,len); - - /* Always schedule a write when the write buffer is non-empty */ - _EL_ADD_WRITE(ac); - - return REDIS_OK; -oom: - __redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory"); - return REDIS_ERR; -} - -int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) { - char *cmd; - int len; - int status; - len = redisvFormatCommand(&cmd,format,ap); - - /* We don't want to pass -1 or -2 to future functions as a length. */ - if (len < 0) - return REDIS_ERR; - - status = __redisAsyncCommand(ac,fn,privdata,cmd,len); - hi_free(cmd); - return status; -} - -int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) { - va_list ap; - int status; - va_start(ap,format); - status = redisvAsyncCommand(ac,fn,privdata,format,ap); - va_end(ap); - return status; -} - -int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) { - sds cmd; - int len; - int status; - len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen); - if (len < 0) - return REDIS_ERR; - status = __redisAsyncCommand(ac,fn,privdata,cmd,len); - sdsfree(cmd); - return status; -} - -int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) { - int status = __redisAsyncCommand(ac,fn,privdata,cmd,len); - return status; -} - -redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn) { - redisAsyncPushFn *old = ac->push_cb; - ac->push_cb = fn; - return old; -} - -int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) { - if (!ac->c.command_timeout) { - ac->c.command_timeout = hi_calloc(1, sizeof(tv)); - if (ac->c.command_timeout == NULL) { - __redisSetError(&ac->c, REDIS_ERR_OOM, "Out of memory"); - __redisAsyncCopyError(ac); - return REDIS_ERR; - } - } - - if (tv.tv_sec != ac->c.command_timeout->tv_sec || - tv.tv_usec != ac->c.command_timeout->tv_usec) - { - *ac->c.command_timeout = tv; - } - - return REDIS_OK; -} diff --git a/ext/hiredis-1.0.2/async.h b/ext/hiredis-1.0.2/async.h deleted file mode 100644 index b1d2cb263..000000000 --- a/ext/hiredis-1.0.2/async.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_ASYNC_H -#define __HIREDIS_ASYNC_H -#include "hiredis.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct redisAsyncContext; /* need forward declaration of redisAsyncContext */ -struct dict; /* dictionary header is included in async.c */ - -/* Reply callback prototype and container */ -typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*); -typedef struct redisCallback { - struct redisCallback *next; /* simple singly linked list */ - redisCallbackFn *fn; - int pending_subs; - void *privdata; -} redisCallback; - -/* List of callbacks for either regular replies or pub/sub */ -typedef struct redisCallbackList { - redisCallback *head, *tail; -} redisCallbackList; - -/* Connection callback prototypes */ -typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status); -typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status); -typedef void(redisTimerCallback)(void *timer, void *privdata); - -/* Context for an async connection to Redis */ -typedef struct redisAsyncContext { - /* Hold the regular context, so it can be realloc'ed. */ - redisContext c; - - /* Setup error flags so they can be used directly. */ - int err; - char *errstr; - - /* Not used by hiredis */ - void *data; - void (*dataCleanup)(void *privdata); - - /* Event library data and hooks */ - struct { - void *data; - - /* Hooks that are called when the library expects to start - * reading/writing. These functions should be idempotent. */ - void (*addRead)(void *privdata); - void (*delRead)(void *privdata); - void (*addWrite)(void *privdata); - void (*delWrite)(void *privdata); - void (*cleanup)(void *privdata); - void (*scheduleTimer)(void *privdata, struct timeval tv); - } ev; - - /* Called when either the connection is terminated due to an error or per - * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */ - redisDisconnectCallback *onDisconnect; - - /* Called when the first write event was received. */ - redisConnectCallback *onConnect; - - /* Regular command callbacks */ - redisCallbackList replies; - - /* Address used for connect() */ - struct sockaddr *saddr; - size_t addrlen; - - /* Subscription callbacks */ - struct { - redisCallbackList invalid; - struct dict *channels; - struct dict *patterns; - } sub; - - /* Any configured RESP3 PUSH handler */ - redisAsyncPushFn *push_cb; -} redisAsyncContext; - -/* Functions that proxy to hiredis */ -redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options); -redisAsyncContext *redisAsyncConnect(const char *ip, int port); -redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr); -redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, - const char *source_addr); -redisAsyncContext *redisAsyncConnectUnix(const char *path); -int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); -int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); - -redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn); -int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv); -void redisAsyncDisconnect(redisAsyncContext *ac); -void redisAsyncFree(redisAsyncContext *ac); - -/* Handle read/write events */ -void redisAsyncHandleRead(redisAsyncContext *ac); -void redisAsyncHandleWrite(redisAsyncContext *ac); -void redisAsyncHandleTimeout(redisAsyncContext *ac); -void redisAsyncRead(redisAsyncContext *ac); -void redisAsyncWrite(redisAsyncContext *ac); - -/* Command functions for an async context. Write the command to the - * output buffer and register the provided callback. */ -int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap); -int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...); -int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen); -int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/ext/hiredis-1.0.2/async_private.h b/ext/hiredis-1.0.2/async_private.h deleted file mode 100644 index b9d23fffd..000000000 --- a/ext/hiredis-1.0.2/async_private.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_ASYNC_PRIVATE_H -#define __HIREDIS_ASYNC_PRIVATE_H - -#define _EL_ADD_READ(ctx) \ - do { \ - refreshTimeout(ctx); \ - if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \ - } while (0) -#define _EL_DEL_READ(ctx) do { \ - if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \ - } while(0) -#define _EL_ADD_WRITE(ctx) \ - do { \ - refreshTimeout(ctx); \ - if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \ - } while (0) -#define _EL_DEL_WRITE(ctx) do { \ - if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \ - } while(0) -#define _EL_CLEANUP(ctx) do { \ - if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \ - ctx->ev.cleanup = NULL; \ - } while(0); - -static inline void refreshTimeout(redisAsyncContext *ctx) { - #define REDIS_TIMER_ISSET(tvp) \ - (tvp && ((tvp)->tv_sec || (tvp)->tv_usec)) - - #define REDIS_EL_TIMER(ac, tvp) \ - if ((ac)->ev.scheduleTimer && REDIS_TIMER_ISSET(tvp)) { \ - (ac)->ev.scheduleTimer((ac)->ev.data, *(tvp)); \ - } - - if (ctx->c.flags & REDIS_CONNECTED) { - REDIS_EL_TIMER(ctx, ctx->c.command_timeout); - } else { - REDIS_EL_TIMER(ctx, ctx->c.connect_timeout); - } -} - -void __redisAsyncDisconnect(redisAsyncContext *ac); -void redisProcessCallbacks(redisAsyncContext *ac); - -#endif /* __HIREDIS_ASYNC_PRIVATE_H */ diff --git a/ext/hiredis-1.0.2/dict.c b/ext/hiredis-1.0.2/dict.c deleted file mode 100644 index 34a33ead9..000000000 --- a/ext/hiredis-1.0.2/dict.c +++ /dev/null @@ -1,352 +0,0 @@ -/* Hash table implementation. - * - * This file implements in memory hash tables with insert/del/replace/find/ - * get-random-element operations. Hash tables will auto resize if needed - * tables of power of two in size are used, collisions are handled by - * chaining. See the source code for more information... :) - * - * Copyright (c) 2006-2010, Salvatore Sanfilippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "fmacros.h" -#include "alloc.h" -#include -#include -#include -#include "dict.h" - -/* -------------------------- private prototypes ---------------------------- */ - -static int _dictExpandIfNeeded(dict *ht); -static unsigned long _dictNextPower(unsigned long size); -static int _dictKeyIndex(dict *ht, const void *key); -static int _dictInit(dict *ht, dictType *type, void *privDataPtr); - -/* -------------------------- hash functions -------------------------------- */ - -/* Generic hash function (a popular one from Bernstein). - * I tested a few and this was the best. */ -static unsigned int dictGenHashFunction(const unsigned char *buf, int len) { - unsigned int hash = 5381; - - while (len--) - hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */ - return hash; -} - -/* ----------------------------- API implementation ------------------------- */ - -/* Reset an hashtable already initialized with ht_init(). - * NOTE: This function should only called by ht_destroy(). */ -static void _dictReset(dict *ht) { - ht->table = NULL; - ht->size = 0; - ht->sizemask = 0; - ht->used = 0; -} - -/* Create a new hash table */ -static dict *dictCreate(dictType *type, void *privDataPtr) { - dict *ht = hi_malloc(sizeof(*ht)); - if (ht == NULL) - return NULL; - - _dictInit(ht,type,privDataPtr); - return ht; -} - -/* Initialize the hash table */ -static int _dictInit(dict *ht, dictType *type, void *privDataPtr) { - _dictReset(ht); - ht->type = type; - ht->privdata = privDataPtr; - return DICT_OK; -} - -/* Expand or create the hashtable */ -static int dictExpand(dict *ht, unsigned long size) { - dict n; /* the new hashtable */ - unsigned long realsize = _dictNextPower(size), i; - - /* the size is invalid if it is smaller than the number of - * elements already inside the hashtable */ - if (ht->used > size) - return DICT_ERR; - - _dictInit(&n, ht->type, ht->privdata); - n.size = realsize; - n.sizemask = realsize-1; - n.table = hi_calloc(realsize,sizeof(dictEntry*)); - if (n.table == NULL) - return DICT_ERR; - - /* Copy all the elements from the old to the new table: - * note that if the old hash table is empty ht->size is zero, - * so dictExpand just creates an hash table. */ - n.used = ht->used; - for (i = 0; i < ht->size && ht->used > 0; i++) { - dictEntry *he, *nextHe; - - if (ht->table[i] == NULL) continue; - - /* For each hash entry on this slot... */ - he = ht->table[i]; - while(he) { - unsigned int h; - - nextHe = he->next; - /* Get the new element index */ - h = dictHashKey(ht, he->key) & n.sizemask; - he->next = n.table[h]; - n.table[h] = he; - ht->used--; - /* Pass to the next element */ - he = nextHe; - } - } - assert(ht->used == 0); - hi_free(ht->table); - - /* Remap the new hashtable in the old */ - *ht = n; - return DICT_OK; -} - -/* Add an element to the target hash table */ -static int dictAdd(dict *ht, void *key, void *val) { - int index; - dictEntry *entry; - - /* Get the index of the new element, or -1 if - * the element already exists. */ - if ((index = _dictKeyIndex(ht, key)) == -1) - return DICT_ERR; - - /* Allocates the memory and stores key */ - entry = hi_malloc(sizeof(*entry)); - if (entry == NULL) - return DICT_ERR; - - entry->next = ht->table[index]; - ht->table[index] = entry; - - /* Set the hash entry fields. */ - dictSetHashKey(ht, entry, key); - dictSetHashVal(ht, entry, val); - ht->used++; - return DICT_OK; -} - -/* Add an element, discarding the old if the key already exists. - * Return 1 if the key was added from scratch, 0 if there was already an - * element with such key and dictReplace() just performed a value update - * operation. */ -static int dictReplace(dict *ht, void *key, void *val) { - dictEntry *entry, auxentry; - - /* Try to add the element. If the key - * does not exists dictAdd will succeed. */ - if (dictAdd(ht, key, val) == DICT_OK) - return 1; - /* It already exists, get the entry */ - entry = dictFind(ht, key); - if (entry == NULL) - return 0; - - /* Free the old value and set the new one */ - /* Set the new value and free the old one. Note that it is important - * to do that in this order, as the value may just be exactly the same - * as the previous one. In this context, think to reference counting, - * you want to increment (set), and then decrement (free), and not the - * reverse. */ - auxentry = *entry; - dictSetHashVal(ht, entry, val); - dictFreeEntryVal(ht, &auxentry); - return 0; -} - -/* Search and remove an element */ -static int dictDelete(dict *ht, const void *key) { - unsigned int h; - dictEntry *de, *prevde; - - if (ht->size == 0) - return DICT_ERR; - h = dictHashKey(ht, key) & ht->sizemask; - de = ht->table[h]; - - prevde = NULL; - while(de) { - if (dictCompareHashKeys(ht,key,de->key)) { - /* Unlink the element from the list */ - if (prevde) - prevde->next = de->next; - else - ht->table[h] = de->next; - - dictFreeEntryKey(ht,de); - dictFreeEntryVal(ht,de); - hi_free(de); - ht->used--; - return DICT_OK; - } - prevde = de; - de = de->next; - } - return DICT_ERR; /* not found */ -} - -/* Destroy an entire hash table */ -static int _dictClear(dict *ht) { - unsigned long i; - - /* Free all the elements */ - for (i = 0; i < ht->size && ht->used > 0; i++) { - dictEntry *he, *nextHe; - - if ((he = ht->table[i]) == NULL) continue; - while(he) { - nextHe = he->next; - dictFreeEntryKey(ht, he); - dictFreeEntryVal(ht, he); - hi_free(he); - ht->used--; - he = nextHe; - } - } - /* Free the table and the allocated cache structure */ - hi_free(ht->table); - /* Re-initialize the table */ - _dictReset(ht); - return DICT_OK; /* never fails */ -} - -/* Clear & Release the hash table */ -static void dictRelease(dict *ht) { - _dictClear(ht); - hi_free(ht); -} - -static dictEntry *dictFind(dict *ht, const void *key) { - dictEntry *he; - unsigned int h; - - if (ht->size == 0) return NULL; - h = dictHashKey(ht, key) & ht->sizemask; - he = ht->table[h]; - while(he) { - if (dictCompareHashKeys(ht, key, he->key)) - return he; - he = he->next; - } - return NULL; -} - -static dictIterator *dictGetIterator(dict *ht) { - dictIterator *iter = hi_malloc(sizeof(*iter)); - if (iter == NULL) - return NULL; - - iter->ht = ht; - iter->index = -1; - iter->entry = NULL; - iter->nextEntry = NULL; - return iter; -} - -static dictEntry *dictNext(dictIterator *iter) { - while (1) { - if (iter->entry == NULL) { - iter->index++; - if (iter->index >= - (signed)iter->ht->size) break; - iter->entry = iter->ht->table[iter->index]; - } else { - iter->entry = iter->nextEntry; - } - if (iter->entry) { - /* We need to save the 'next' here, the iterator user - * may delete the entry we are returning. */ - iter->nextEntry = iter->entry->next; - return iter->entry; - } - } - return NULL; -} - -static void dictReleaseIterator(dictIterator *iter) { - hi_free(iter); -} - -/* ------------------------- private functions ------------------------------ */ - -/* Expand the hash table if needed */ -static int _dictExpandIfNeeded(dict *ht) { - /* If the hash table is empty expand it to the initial size, - * if the table is "full" double its size. */ - if (ht->size == 0) - return dictExpand(ht, DICT_HT_INITIAL_SIZE); - if (ht->used == ht->size) - return dictExpand(ht, ht->size*2); - return DICT_OK; -} - -/* Our hash table capability is a power of two */ -static unsigned long _dictNextPower(unsigned long size) { - unsigned long i = DICT_HT_INITIAL_SIZE; - - if (size >= LONG_MAX) return LONG_MAX; - while(1) { - if (i >= size) - return i; - i *= 2; - } -} - -/* Returns the index of a free slot that can be populated with - * an hash entry for the given 'key'. - * If the key already exists, -1 is returned. */ -static int _dictKeyIndex(dict *ht, const void *key) { - unsigned int h; - dictEntry *he; - - /* Expand the hashtable if needed */ - if (_dictExpandIfNeeded(ht) == DICT_ERR) - return -1; - /* Compute the key hash value */ - h = dictHashKey(ht, key) & ht->sizemask; - /* Search if this slot does not already contain the given key */ - he = ht->table[h]; - while(he) { - if (dictCompareHashKeys(ht, key, he->key)) - return -1; - he = he->next; - } - return h; -} - diff --git a/ext/hiredis-1.0.2/dict.h b/ext/hiredis-1.0.2/dict.h deleted file mode 100644 index 95fcd280e..000000000 --- a/ext/hiredis-1.0.2/dict.h +++ /dev/null @@ -1,126 +0,0 @@ -/* Hash table implementation. - * - * This file implements in memory hash tables with insert/del/replace/find/ - * get-random-element operations. Hash tables will auto resize if needed - * tables of power of two in size are used, collisions are handled by - * chaining. See the source code for more information... :) - * - * Copyright (c) 2006-2010, Salvatore Sanfilippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __DICT_H -#define __DICT_H - -#define DICT_OK 0 -#define DICT_ERR 1 - -/* Unused arguments generate annoying warnings... */ -#define DICT_NOTUSED(V) ((void) V) - -typedef struct dictEntry { - void *key; - void *val; - struct dictEntry *next; -} dictEntry; - -typedef struct dictType { - unsigned int (*hashFunction)(const void *key); - void *(*keyDup)(void *privdata, const void *key); - void *(*valDup)(void *privdata, const void *obj); - int (*keyCompare)(void *privdata, const void *key1, const void *key2); - void (*keyDestructor)(void *privdata, void *key); - void (*valDestructor)(void *privdata, void *obj); -} dictType; - -typedef struct dict { - dictEntry **table; - dictType *type; - unsigned long size; - unsigned long sizemask; - unsigned long used; - void *privdata; -} dict; - -typedef struct dictIterator { - dict *ht; - int index; - dictEntry *entry, *nextEntry; -} dictIterator; - -/* This is the initial size of every hash table */ -#define DICT_HT_INITIAL_SIZE 4 - -/* ------------------------------- Macros ------------------------------------*/ -#define dictFreeEntryVal(ht, entry) \ - if ((ht)->type->valDestructor) \ - (ht)->type->valDestructor((ht)->privdata, (entry)->val) - -#define dictSetHashVal(ht, entry, _val_) do { \ - if ((ht)->type->valDup) \ - entry->val = (ht)->type->valDup((ht)->privdata, _val_); \ - else \ - entry->val = (_val_); \ -} while(0) - -#define dictFreeEntryKey(ht, entry) \ - if ((ht)->type->keyDestructor) \ - (ht)->type->keyDestructor((ht)->privdata, (entry)->key) - -#define dictSetHashKey(ht, entry, _key_) do { \ - if ((ht)->type->keyDup) \ - entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \ - else \ - entry->key = (_key_); \ -} while(0) - -#define dictCompareHashKeys(ht, key1, key2) \ - (((ht)->type->keyCompare) ? \ - (ht)->type->keyCompare((ht)->privdata, key1, key2) : \ - (key1) == (key2)) - -#define dictHashKey(ht, key) (ht)->type->hashFunction(key) - -#define dictGetEntryKey(he) ((he)->key) -#define dictGetEntryVal(he) ((he)->val) -#define dictSlots(ht) ((ht)->size) -#define dictSize(ht) ((ht)->used) - -/* API */ -static unsigned int dictGenHashFunction(const unsigned char *buf, int len); -static dict *dictCreate(dictType *type, void *privDataPtr); -static int dictExpand(dict *ht, unsigned long size); -static int dictAdd(dict *ht, void *key, void *val); -static int dictReplace(dict *ht, void *key, void *val); -static int dictDelete(dict *ht, const void *key); -static void dictRelease(dict *ht); -static dictEntry * dictFind(dict *ht, const void *key); -static dictIterator *dictGetIterator(dict *ht); -static dictEntry *dictNext(dictIterator *iter); -static void dictReleaseIterator(dictIterator *iter); - -#endif /* __DICT_H */ diff --git a/ext/hiredis-1.0.2/examples/CMakeLists.txt b/ext/hiredis-1.0.2/examples/CMakeLists.txt deleted file mode 100644 index 1d5bc56e0..000000000 --- a/ext/hiredis-1.0.2/examples/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -INCLUDE(FindPkgConfig) -# Check for GLib - -PKG_CHECK_MODULES(GLIB2 glib-2.0) -if (GLIB2_FOUND) - INCLUDE_DIRECTORIES(${GLIB2_INCLUDE_DIRS}) - LINK_DIRECTORIES(${GLIB2_LIBRARY_DIRS}) - ADD_EXECUTABLE(example-glib example-glib.c) - TARGET_LINK_LIBRARIES(example-glib hiredis ${GLIB2_LIBRARIES}) -ENDIF(GLIB2_FOUND) - -FIND_PATH(LIBEV ev.h - HINTS /usr/local /usr/opt/local - ENV LIBEV_INCLUDE_DIR) - -if (LIBEV) - # Just compile and link with libev - ADD_EXECUTABLE(example-libev example-libev.c) - TARGET_LINK_LIBRARIES(example-libev hiredis ev) -ENDIF() - -FIND_PATH(LIBEVENT event.h) -if (LIBEVENT) - ADD_EXECUTABLE(example-libevent example-libevent) - TARGET_LINK_LIBRARIES(example-libevent hiredis event) -ENDIF() - -FIND_PATH(LIBUV uv.h) -IF (LIBUV) - ADD_EXECUTABLE(example-libuv example-libuv.c) - TARGET_LINK_LIBRARIES(example-libuv hiredis uv) -ENDIF() - -IF (APPLE) - FIND_LIBRARY(CF CoreFoundation) - ADD_EXECUTABLE(example-macosx example-macosx.c) - TARGET_LINK_LIBRARIES(example-macosx hiredis ${CF}) -ENDIF() - -IF (ENABLE_SSL) - ADD_EXECUTABLE(example-ssl example-ssl.c) - TARGET_LINK_LIBRARIES(example-ssl hiredis hiredis_ssl) -ENDIF() - -ADD_EXECUTABLE(example example.c) -TARGET_LINK_LIBRARIES(example hiredis) - -ADD_EXECUTABLE(example-push example-push.c) -TARGET_LINK_LIBRARIES(example-push hiredis) diff --git a/ext/hiredis-1.0.2/examples/example-ae.c b/ext/hiredis-1.0.2/examples/example-ae.c deleted file mode 100644 index 8efa7306a..000000000 --- a/ext/hiredis-1.0.2/examples/example-ae.c +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -/* Put event loop in the global scope, so it can be explicitly stopped */ -static aeEventLoop *loop; - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - aeStop(loop); - return; - } - - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - aeStop(loop); - return; - } - - printf("Disconnected...\n"); - aeStop(loop); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - loop = aeCreateEventLoop(64); - redisAeAttach(loop, c); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - aeMain(loop); - return 0; -} - diff --git a/ext/hiredis-1.0.2/examples/example-glib.c b/ext/hiredis-1.0.2/examples/example-glib.c deleted file mode 100644 index d6e10f8e8..000000000 --- a/ext/hiredis-1.0.2/examples/example-glib.c +++ /dev/null @@ -1,73 +0,0 @@ -#include - -#include -#include -#include - -static GMainLoop *mainloop; - -static void -connect_cb (const redisAsyncContext *ac G_GNUC_UNUSED, - int status) -{ - if (status != REDIS_OK) { - g_printerr("Failed to connect: %s\n", ac->errstr); - g_main_loop_quit(mainloop); - } else { - g_printerr("Connected...\n"); - } -} - -static void -disconnect_cb (const redisAsyncContext *ac G_GNUC_UNUSED, - int status) -{ - if (status != REDIS_OK) { - g_error("Failed to disconnect: %s", ac->errstr); - } else { - g_printerr("Disconnected...\n"); - g_main_loop_quit(mainloop); - } -} - -static void -command_cb(redisAsyncContext *ac, - gpointer r, - gpointer user_data G_GNUC_UNUSED) -{ - redisReply *reply = r; - - if (reply) { - g_print("REPLY: %s\n", reply->str); - } - - redisAsyncDisconnect(ac); -} - -gint -main (gint argc G_GNUC_UNUSED, - gchar *argv[] G_GNUC_UNUSED) -{ - redisAsyncContext *ac; - GMainContext *context = NULL; - GSource *source; - - ac = redisAsyncConnect("127.0.0.1", 6379); - if (ac->err) { - g_printerr("%s\n", ac->errstr); - exit(EXIT_FAILURE); - } - - source = redis_source_new(ac); - mainloop = g_main_loop_new(context, FALSE); - g_source_attach(source, context); - - redisAsyncSetConnectCallback(ac, connect_cb); - redisAsyncSetDisconnectCallback(ac, disconnect_cb); - redisAsyncCommand(ac, command_cb, NULL, "SET key 1234"); - redisAsyncCommand(ac, command_cb, NULL, "GET key"); - - g_main_loop_run(mainloop); - - return EXIT_SUCCESS; -} diff --git a/ext/hiredis-1.0.2/examples/example-ivykis.c b/ext/hiredis-1.0.2/examples/example-ivykis.c deleted file mode 100644 index f57dc3887..000000000 --- a/ext/hiredis-1.0.2/examples/example-ivykis.c +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { -#ifndef _WIN32 - signal(SIGPIPE, SIG_IGN); -#endif - - iv_init(); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisIvykisAttach(c); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - - iv_main(); - - iv_deinit(); - - return 0; -} diff --git a/ext/hiredis-1.0.2/examples/example-libev.c b/ext/hiredis-1.0.2/examples/example-libev.c deleted file mode 100644 index ec474306b..000000000 --- a/ext/hiredis-1.0.2/examples/example-libev.c +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { -#ifndef _WIN32 - signal(SIGPIPE, SIG_IGN); -#endif - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisLibevAttach(EV_DEFAULT_ c); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - ev_loop(EV_DEFAULT_ 0); - return 0; -} diff --git a/ext/hiredis-1.0.2/examples/example-libevent-ssl.c b/ext/hiredis-1.0.2/examples/example-libevent-ssl.c deleted file mode 100644 index 7d99af1ba..000000000 --- a/ext/hiredis-1.0.2/examples/example-libevent-ssl.c +++ /dev/null @@ -1,90 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { -#ifndef _WIN32 - signal(SIGPIPE, SIG_IGN); -#endif - - struct event_base *base = event_base_new(); - if (argc < 5) { - fprintf(stderr, - "Usage: %s [ca]\n", argv[0]); - exit(1); - } - - const char *value = argv[1]; - size_t nvalue = strlen(value); - - const char *hostname = argv[2]; - int port = atoi(argv[3]); - - const char *cert = argv[4]; - const char *certKey = argv[5]; - const char *caCert = argc > 5 ? argv[6] : NULL; - - redisSSLContext *ssl; - redisSSLContextError ssl_error; - - redisInitOpenSSL(); - - ssl = redisCreateSSLContext(caCert, NULL, - cert, certKey, NULL, &ssl_error); - if (!ssl) { - printf("Error: %s\n", redisSSLContextGetError(ssl_error)); - return 1; - } - - redisAsyncContext *c = redisAsyncConnect(hostname, port); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - if (redisInitiateSSLWithContext(&c->c, ssl) != REDIS_OK) { - printf("SSL Error!\n"); - exit(1); - } - - redisLibeventAttach(c,base); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", value, nvalue); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - event_base_dispatch(base); - - redisFreeSSLContext(ssl); - return 0; -} diff --git a/ext/hiredis-1.0.2/examples/example-libevent.c b/ext/hiredis-1.0.2/examples/example-libevent.c deleted file mode 100644 index 49bddd0c2..000000000 --- a/ext/hiredis-1.0.2/examples/example-libevent.c +++ /dev/null @@ -1,67 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) { - if (c->errstr) { - printf("errstr: %s\n", c->errstr); - } - return; - } - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { -#ifndef _WIN32 - signal(SIGPIPE, SIG_IGN); -#endif - - struct event_base *base = event_base_new(); - redisOptions options = {0}; - REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379); - struct timeval tv = {0}; - tv.tv_sec = 1; - options.connect_timeout = &tv; - - - redisAsyncContext *c = redisAsyncConnectWithOptions(&options); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisLibeventAttach(c,base); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - event_base_dispatch(base); - return 0; -} diff --git a/ext/hiredis-1.0.2/examples/example-libuv.c b/ext/hiredis-1.0.2/examples/example-libuv.c deleted file mode 100644 index cbde452b9..000000000 --- a/ext/hiredis-1.0.2/examples/example-libuv.c +++ /dev/null @@ -1,56 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { -#ifndef _WIN32 - signal(SIGPIPE, SIG_IGN); -#endif - - uv_loop_t* loop = uv_default_loop(); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisLibuvAttach(c,loop); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - uv_run(loop, UV_RUN_DEFAULT); - return 0; -} diff --git a/ext/hiredis-1.0.2/examples/example-macosx.c b/ext/hiredis-1.0.2/examples/example-macosx.c deleted file mode 100644 index bc84ed5ba..000000000 --- a/ext/hiredis-1.0.2/examples/example-macosx.c +++ /dev/null @@ -1,66 +0,0 @@ -// -// Created by Дмитрий Бахвалов on 13.07.15. -// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved. -// - -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - CFRunLoopStop(CFRunLoopGetCurrent()); - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - - CFRunLoopRef loop = CFRunLoopGetCurrent(); - if( !loop ) { - printf("Error: Cannot get current run loop\n"); - return 1; - } - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisMacOSAttach(c, loop); - - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - - CFRunLoopRun(); - - return 0; -} - diff --git a/ext/hiredis-1.0.2/examples/example-push.c b/ext/hiredis-1.0.2/examples/example-push.c deleted file mode 100644 index 2d4ab4dc0..000000000 --- a/ext/hiredis-1.0.2/examples/example-push.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2020, Michael Grunder - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#include -#include -#include -#include -#include - -#define KEY_COUNT 5 - -#define panicAbort(fmt, ...) \ - do { \ - fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, __VA_ARGS__); \ - exit(-1); \ - } while (0) - -static void assertReplyAndFree(redisContext *context, redisReply *reply, int type) { - if (reply == NULL) - panicAbort("NULL reply from server (error: %s)", context->errstr); - - if (reply->type != type) { - if (reply->type == REDIS_REPLY_ERROR) - fprintf(stderr, "Redis Error: %s\n", reply->str); - - panicAbort("Expected reply type %d but got type %d", type, reply->type); - } - - freeReplyObject(reply); -} - -/* Switch to the RESP3 protocol and enable client tracking */ -static void enableClientTracking(redisContext *c) { - redisReply *reply = redisCommand(c, "HELLO 3"); - if (reply == NULL || c->err) { - panicAbort("NULL reply or server error (error: %s)", c->errstr); - } - - if (reply->type != REDIS_REPLY_MAP) { - fprintf(stderr, "Error: Can't send HELLO 3 command. Are you sure you're "); - fprintf(stderr, "connected to redis-server >= 6.0.0?\nRedis error: %s\n", - reply->type == REDIS_REPLY_ERROR ? reply->str : "(unknown)"); - exit(-1); - } - - freeReplyObject(reply); - - /* Enable client tracking */ - reply = redisCommand(c, "CLIENT TRACKING ON"); - assertReplyAndFree(c, reply, REDIS_REPLY_STATUS); -} - -void pushReplyHandler(void *privdata, void *r) { - redisReply *reply = r; - int *invalidations = privdata; - - /* Sanity check on the invalidation reply */ - if (reply->type != REDIS_REPLY_PUSH || reply->elements != 2 || - reply->element[1]->type != REDIS_REPLY_ARRAY || - reply->element[1]->element[0]->type != REDIS_REPLY_STRING) - { - panicAbort("%s", "Can't parse PUSH message!"); - } - - /* Increment our invalidation count */ - *invalidations += 1; - - printf("pushReplyHandler(): INVALIDATE '%s' (invalidation count: %d)\n", - reply->element[1]->element[0]->str, *invalidations); - - freeReplyObject(reply); -} - -/* We aren't actually freeing anything here, but it is included to show that we can - * have hiredis call our data destructor when freeing the context */ -void privdata_dtor(void *privdata) { - unsigned int *icount = privdata; - printf("privdata_dtor(): In context privdata dtor (invalidations: %u)\n", *icount); -} - -int main(int argc, char **argv) { - unsigned int j, invalidations = 0; - redisContext *c; - redisReply *reply; - - const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1"; - int port = (argc > 2) ? atoi(argv[2]) : 6379; - - redisOptions o = {0}; - REDIS_OPTIONS_SET_TCP(&o, hostname, port); - - /* Set our context privdata to the address of our invalidation counter. Each - * time our PUSH handler is called, hiredis will pass the privdata for context. - * - * This could also be done after we create the context like so: - * - * c->privdata = &invalidations; - * c->free_privdata = privdata_dtor; - */ - REDIS_OPTIONS_SET_PRIVDATA(&o, &invalidations, privdata_dtor); - - /* Set our custom PUSH message handler */ - o.push_cb = pushReplyHandler; - - c = redisConnectWithOptions(&o); - if (c == NULL || c->err) - panicAbort("Connection error: %s", c ? c->errstr : "OOM"); - - /* Enable RESP3 and turn on client tracking */ - enableClientTracking(c); - - /* Set some keys and then read them back. Once we do that, Redis will deliver - * invalidation push messages whenever the key is modified */ - for (j = 0; j < KEY_COUNT; j++) { - reply = redisCommand(c, "SET key:%d initial:%d", j, j); - assertReplyAndFree(c, reply, REDIS_REPLY_STATUS); - - reply = redisCommand(c, "GET key:%d", j); - assertReplyAndFree(c, reply, REDIS_REPLY_STRING); - } - - /* Trigger invalidation messages by updating keys we just read */ - for (j = 0; j < KEY_COUNT; j++) { - printf(" main(): SET key:%d update:%d\n", j, j); - reply = redisCommand(c, "SET key:%d update:%d", j, j); - assertReplyAndFree(c, reply, REDIS_REPLY_STATUS); - printf(" main(): SET REPLY OK\n"); - } - - printf("\nTotal detected invalidations: %d, expected: %d\n", invalidations, KEY_COUNT); - - /* PING server */ - redisFree(c); -} diff --git a/ext/hiredis-1.0.2/examples/example-qt.cpp b/ext/hiredis-1.0.2/examples/example-qt.cpp deleted file mode 100644 index f524c3f3d..000000000 --- a/ext/hiredis-1.0.2/examples/example-qt.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include -using namespace std; - -#include -#include - -#include "example-qt.h" - -void getCallback(redisAsyncContext *, void * r, void * privdata) { - - redisReply * reply = static_cast(r); - ExampleQt * ex = static_cast(privdata); - if (reply == nullptr || ex == nullptr) return; - - cout << "key: " << reply->str << endl; - - ex->finish(); -} - -void ExampleQt::run() { - - m_ctx = redisAsyncConnect("localhost", 6379); - - if (m_ctx->err) { - cerr << "Error: " << m_ctx->errstr << endl; - redisAsyncFree(m_ctx); - emit finished(); - } - - m_adapter.setContext(m_ctx); - - redisAsyncCommand(m_ctx, NULL, NULL, "SET key %s", m_value); - redisAsyncCommand(m_ctx, getCallback, this, "GET key"); -} - -int main (int argc, char **argv) { - - QCoreApplication app(argc, argv); - - ExampleQt example(argv[argc-1]); - - QObject::connect(&example, SIGNAL(finished()), &app, SLOT(quit())); - QTimer::singleShot(0, &example, SLOT(run())); - - return app.exec(); -} diff --git a/ext/hiredis-1.0.2/examples/example-qt.h b/ext/hiredis-1.0.2/examples/example-qt.h deleted file mode 100644 index 374f47666..000000000 --- a/ext/hiredis-1.0.2/examples/example-qt.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __HIREDIS_EXAMPLE_QT_H -#define __HIREDIS_EXAMPLE_QT_H - -#include - -class ExampleQt : public QObject { - - Q_OBJECT - - public: - ExampleQt(const char * value, QObject * parent = 0) - : QObject(parent), m_value(value) {} - - signals: - void finished(); - - public slots: - void run(); - - private: - void finish() { emit finished(); } - - private: - const char * m_value; - redisAsyncContext * m_ctx; - RedisQtAdapter m_adapter; - - friend - void getCallback(redisAsyncContext *, void *, void *); -}; - -#endif /* !__HIREDIS_EXAMPLE_QT_H */ diff --git a/ext/hiredis-1.0.2/examples/example-ssl.c b/ext/hiredis-1.0.2/examples/example-ssl.c deleted file mode 100644 index c754177cf..000000000 --- a/ext/hiredis-1.0.2/examples/example-ssl.c +++ /dev/null @@ -1,110 +0,0 @@ -#include -#include -#include - -#include -#include -#include - -int main(int argc, char **argv) { - unsigned int j; - redisSSLContext *ssl; - redisSSLContextError ssl_error; - redisContext *c; - redisReply *reply; - if (argc < 4) { - printf("Usage: %s [ca]\n", argv[0]); - exit(1); - } - const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1"; - int port = atoi(argv[2]); - const char *cert = argv[3]; - const char *key = argv[4]; - const char *ca = argc > 4 ? argv[5] : NULL; - - redisInitOpenSSL(); - ssl = redisCreateSSLContext(ca, NULL, cert, key, NULL, &ssl_error); - if (!ssl) { - printf("SSL Context error: %s\n", - redisSSLContextGetError(ssl_error)); - exit(1); - } - - struct timeval tv = { 1, 500000 }; // 1.5 seconds - redisOptions options = {0}; - REDIS_OPTIONS_SET_TCP(&options, hostname, port); - options.connect_timeout = &tv; - c = redisConnectWithOptions(&options); - - if (c == NULL || c->err) { - if (c) { - printf("Connection error: %s\n", c->errstr); - redisFree(c); - } else { - printf("Connection error: can't allocate redis context\n"); - } - exit(1); - } - - if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) { - printf("Couldn't initialize SSL!\n"); - printf("Error: %s\n", c->errstr); - redisFree(c); - exit(1); - } - - /* PING server */ - reply = redisCommand(c,"PING"); - printf("PING: %s\n", reply->str); - freeReplyObject(reply); - - /* Set a key */ - reply = redisCommand(c,"SET %s %s", "foo", "hello world"); - printf("SET: %s\n", reply->str); - freeReplyObject(reply); - - /* Set a key using binary safe API */ - reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5); - printf("SET (binary API): %s\n", reply->str); - freeReplyObject(reply); - - /* Try a GET and two INCR */ - reply = redisCommand(c,"GET foo"); - printf("GET foo: %s\n", reply->str); - freeReplyObject(reply); - - reply = redisCommand(c,"INCR counter"); - printf("INCR counter: %lld\n", reply->integer); - freeReplyObject(reply); - /* again ... */ - reply = redisCommand(c,"INCR counter"); - printf("INCR counter: %lld\n", reply->integer); - freeReplyObject(reply); - - /* Create a list of numbers, from 0 to 9 */ - reply = redisCommand(c,"DEL mylist"); - freeReplyObject(reply); - for (j = 0; j < 10; j++) { - char buf[64]; - - snprintf(buf,64,"%u",j); - reply = redisCommand(c,"LPUSH mylist element-%s", buf); - freeReplyObject(reply); - } - - /* Let's check what we have inside the list */ - reply = redisCommand(c,"LRANGE mylist 0 -1"); - if (reply->type == REDIS_REPLY_ARRAY) { - for (j = 0; j < reply->elements; j++) { - printf("%u) %s\n", j, reply->element[j]->str); - } - } - freeReplyObject(reply); - - /* Disconnects and frees the context */ - redisFree(c); - - redisFreeSSLContext(ssl); - - return 0; -} diff --git a/ext/hiredis-1.0.2/examples/example.c b/ext/hiredis-1.0.2/examples/example.c deleted file mode 100644 index 15dacbd18..000000000 --- a/ext/hiredis-1.0.2/examples/example.c +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include -#include -#include - -int main(int argc, char **argv) { - unsigned int j, isunix = 0; - redisContext *c; - redisReply *reply; - const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1"; - - if (argc > 2) { - if (*argv[2] == 'u' || *argv[2] == 'U') { - isunix = 1; - /* in this case, host is the path to the unix socket */ - printf("Will connect to unix socket @%s\n", hostname); - } - } - - int port = (argc > 2) ? atoi(argv[2]) : 6379; - - struct timeval timeout = { 1, 500000 }; // 1.5 seconds - if (isunix) { - c = redisConnectUnixWithTimeout(hostname, timeout); - } else { - c = redisConnectWithTimeout(hostname, port, timeout); - } - if (c == NULL || c->err) { - if (c) { - printf("Connection error: %s\n", c->errstr); - redisFree(c); - } else { - printf("Connection error: can't allocate redis context\n"); - } - exit(1); - } - - /* PING server */ - reply = redisCommand(c,"PING"); - printf("PING: %s\n", reply->str); - freeReplyObject(reply); - - /* Set a key */ - reply = redisCommand(c,"SET %s %s", "foo", "hello world"); - printf("SET: %s\n", reply->str); - freeReplyObject(reply); - - /* Set a key using binary safe API */ - reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5); - printf("SET (binary API): %s\n", reply->str); - freeReplyObject(reply); - - /* Try a GET and two INCR */ - reply = redisCommand(c,"GET foo"); - printf("GET foo: %s\n", reply->str); - freeReplyObject(reply); - - reply = redisCommand(c,"INCR counter"); - printf("INCR counter: %lld\n", reply->integer); - freeReplyObject(reply); - /* again ... */ - reply = redisCommand(c,"INCR counter"); - printf("INCR counter: %lld\n", reply->integer); - freeReplyObject(reply); - - /* Create a list of numbers, from 0 to 9 */ - reply = redisCommand(c,"DEL mylist"); - freeReplyObject(reply); - for (j = 0; j < 10; j++) { - char buf[64]; - - snprintf(buf,64,"%u",j); - reply = redisCommand(c,"LPUSH mylist element-%s", buf); - freeReplyObject(reply); - } - - /* Let's check what we have inside the list */ - reply = redisCommand(c,"LRANGE mylist 0 -1"); - if (reply->type == REDIS_REPLY_ARRAY) { - for (j = 0; j < reply->elements; j++) { - printf("%u) %s\n", j, reply->element[j]->str); - } - } - freeReplyObject(reply); - - /* Disconnects and frees the context */ - redisFree(c); - - return 0; -} diff --git a/ext/hiredis-1.0.2/fmacros.h b/ext/hiredis-1.0.2/fmacros.h deleted file mode 100644 index 3227faafd..000000000 --- a/ext/hiredis-1.0.2/fmacros.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __HIREDIS_FMACRO_H -#define __HIREDIS_FMACRO_H - -#define _XOPEN_SOURCE 600 -#define _POSIX_C_SOURCE 200112L - -#if defined(__APPLE__) && defined(__MACH__) -/* Enable TCP_KEEPALIVE */ -#define _DARWIN_C_SOURCE -#endif - -#endif diff --git a/ext/hiredis-1.0.2/hiredis-config.cmake.in b/ext/hiredis-1.0.2/hiredis-config.cmake.in deleted file mode 100644 index 98851dcee..000000000 --- a/ext/hiredis-1.0.2/hiredis-config.cmake.in +++ /dev/null @@ -1,13 +0,0 @@ -@PACKAGE_INIT@ - -set_and_check(hiredis_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@") - -IF (NOT TARGET hiredis::hiredis) - INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis-targets.cmake) -ENDIF() - -SET(hiredis_LIBRARIES hiredis::hiredis) -SET(hiredis_INCLUDE_DIRS ${hiredis_INCLUDEDIR}) - -check_required_components(hiredis) - diff --git a/ext/hiredis-1.0.2/hiredis.c b/ext/hiredis-1.0.2/hiredis.c deleted file mode 100644 index ab0e39822..000000000 --- a/ext/hiredis-1.0.2/hiredis.c +++ /dev/null @@ -1,1174 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2014, Pieter Noordhuis - * Copyright (c) 2015, Matt Stancliff , - * Jan-Erik Rediger - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "fmacros.h" -#include -#include -#include -#include -#include - -#include "hiredis.h" -#include "net.h" -#include "sds.h" -#include "async.h" -#include "win32.h" - -extern int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout); -extern int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout); - -static redisContextFuncs redisContextDefaultFuncs = { - .free_privctx = NULL, - .async_read = redisAsyncRead, - .async_write = redisAsyncWrite, - .read = redisNetRead, - .write = redisNetWrite -}; - -static redisReply *createReplyObject(int type); -static void *createStringObject(const redisReadTask *task, char *str, size_t len); -static void *createArrayObject(const redisReadTask *task, size_t elements); -static void *createIntegerObject(const redisReadTask *task, long long value); -static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len); -static void *createNilObject(const redisReadTask *task); -static void *createBoolObject(const redisReadTask *task, int bval); - -/* Default set of functions to build the reply. Keep in mind that such a - * function returning NULL is interpreted as OOM. */ -static redisReplyObjectFunctions defaultFunctions = { - createStringObject, - createArrayObject, - createIntegerObject, - createDoubleObject, - createNilObject, - createBoolObject, - freeReplyObject -}; - -/* Create a reply object */ -static redisReply *createReplyObject(int type) { - redisReply *r = hi_calloc(1,sizeof(*r)); - - if (r == NULL) - return NULL; - - r->type = type; - return r; -} - -/* Free a reply object */ -void freeReplyObject(void *reply) { - redisReply *r = reply; - size_t j; - - if (r == NULL) - return; - - switch(r->type) { - case REDIS_REPLY_INTEGER: - break; /* Nothing to free */ - case REDIS_REPLY_ARRAY: - case REDIS_REPLY_MAP: - case REDIS_REPLY_SET: - case REDIS_REPLY_PUSH: - if (r->element != NULL) { - for (j = 0; j < r->elements; j++) - freeReplyObject(r->element[j]); - hi_free(r->element); - } - break; - case REDIS_REPLY_ERROR: - case REDIS_REPLY_STATUS: - case REDIS_REPLY_STRING: - case REDIS_REPLY_DOUBLE: - case REDIS_REPLY_VERB: - hi_free(r->str); - break; - } - hi_free(r); -} - -static void *createStringObject(const redisReadTask *task, char *str, size_t len) { - redisReply *r, *parent; - char *buf; - - r = createReplyObject(task->type); - if (r == NULL) - return NULL; - - assert(task->type == REDIS_REPLY_ERROR || - task->type == REDIS_REPLY_STATUS || - task->type == REDIS_REPLY_STRING || - task->type == REDIS_REPLY_VERB); - - /* Copy string value */ - if (task->type == REDIS_REPLY_VERB) { - buf = hi_malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */ - if (buf == NULL) goto oom; - - memcpy(r->vtype,str,3); - r->vtype[3] = '\0'; - memcpy(buf,str+4,len-4); - buf[len-4] = '\0'; - r->len = len - 4; - } else { - buf = hi_malloc(len+1); - if (buf == NULL) goto oom; - - memcpy(buf,str,len); - buf[len] = '\0'; - r->len = len; - } - r->str = buf; - - if (task->parent) { - parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY || - parent->type == REDIS_REPLY_MAP || - parent->type == REDIS_REPLY_SET || - parent->type == REDIS_REPLY_PUSH); - parent->element[task->idx] = r; - } - return r; - -oom: - freeReplyObject(r); - return NULL; -} - -static void *createArrayObject(const redisReadTask *task, size_t elements) { - redisReply *r, *parent; - - r = createReplyObject(task->type); - if (r == NULL) - return NULL; - - if (elements > 0) { - if (SIZE_MAX / sizeof(redisReply*) < elements) return NULL; /* Don't overflow */ - r->element = hi_calloc(elements,sizeof(redisReply*)); - if (r->element == NULL) { - freeReplyObject(r); - return NULL; - } - } - - r->elements = elements; - - if (task->parent) { - parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY || - parent->type == REDIS_REPLY_MAP || - parent->type == REDIS_REPLY_SET || - parent->type == REDIS_REPLY_PUSH); - parent->element[task->idx] = r; - } - return r; -} - -static void *createIntegerObject(const redisReadTask *task, long long value) { - redisReply *r, *parent; - - r = createReplyObject(REDIS_REPLY_INTEGER); - if (r == NULL) - return NULL; - - r->integer = value; - - if (task->parent) { - parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY || - parent->type == REDIS_REPLY_MAP || - parent->type == REDIS_REPLY_SET || - parent->type == REDIS_REPLY_PUSH); - parent->element[task->idx] = r; - } - return r; -} - -static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len) { - redisReply *r, *parent; - - r = createReplyObject(REDIS_REPLY_DOUBLE); - if (r == NULL) - return NULL; - - r->dval = value; - r->str = hi_malloc(len+1); - if (r->str == NULL) { - freeReplyObject(r); - return NULL; - } - - /* The double reply also has the original protocol string representing a - * double as a null terminated string. This way the caller does not need - * to format back for string conversion, especially since Redis does efforts - * to make the string more human readable avoiding the calssical double - * decimal string conversion artifacts. */ - memcpy(r->str, str, len); - r->str[len] = '\0'; - - if (task->parent) { - parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY || - parent->type == REDIS_REPLY_MAP || - parent->type == REDIS_REPLY_SET); - parent->element[task->idx] = r; - } - return r; -} - -static void *createNilObject(const redisReadTask *task) { - redisReply *r, *parent; - - r = createReplyObject(REDIS_REPLY_NIL); - if (r == NULL) - return NULL; - - if (task->parent) { - parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY || - parent->type == REDIS_REPLY_MAP || - parent->type == REDIS_REPLY_SET); - parent->element[task->idx] = r; - } - return r; -} - -static void *createBoolObject(const redisReadTask *task, int bval) { - redisReply *r, *parent; - - r = createReplyObject(REDIS_REPLY_BOOL); - if (r == NULL) - return NULL; - - r->integer = bval != 0; - - if (task->parent) { - parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY || - parent->type == REDIS_REPLY_MAP || - parent->type == REDIS_REPLY_SET); - parent->element[task->idx] = r; - } - return r; -} - -/* Return the number of digits of 'v' when converted to string in radix 10. - * Implementation borrowed from link in redis/src/util.c:string2ll(). */ -static uint32_t countDigits(uint64_t v) { - uint32_t result = 1; - for (;;) { - if (v < 10) return result; - if (v < 100) return result + 1; - if (v < 1000) return result + 2; - if (v < 10000) return result + 3; - v /= 10000U; - result += 4; - } -} - -/* Helper that calculates the bulk length given a certain string length. */ -static size_t bulklen(size_t len) { - return 1+countDigits(len)+2+len+2; -} - -int redisvFormatCommand(char **target, const char *format, va_list ap) { - const char *c = format; - char *cmd = NULL; /* final command */ - int pos; /* position in final command */ - sds curarg, newarg; /* current argument */ - int touched = 0; /* was the current argument touched? */ - char **curargv = NULL, **newargv = NULL; - int argc = 0; - int totlen = 0; - int error_type = 0; /* 0 = no error; -1 = memory error; -2 = format error */ - int j; - - /* Abort if there is not target to set */ - if (target == NULL) - return -1; - - /* Build the command string accordingly to protocol */ - curarg = sdsempty(); - if (curarg == NULL) - return -1; - - while(*c != '\0') { - if (*c != '%' || c[1] == '\0') { - if (*c == ' ') { - if (touched) { - newargv = hi_realloc(curargv,sizeof(char*)*(argc+1)); - if (newargv == NULL) goto memory_err; - curargv = newargv; - curargv[argc++] = curarg; - totlen += bulklen(sdslen(curarg)); - - /* curarg is put in argv so it can be overwritten. */ - curarg = sdsempty(); - if (curarg == NULL) goto memory_err; - touched = 0; - } - } else { - newarg = sdscatlen(curarg,c,1); - if (newarg == NULL) goto memory_err; - curarg = newarg; - touched = 1; - } - } else { - char *arg; - size_t size; - - /* Set newarg so it can be checked even if it is not touched. */ - newarg = curarg; - - switch(c[1]) { - case 's': - arg = va_arg(ap,char*); - size = strlen(arg); - if (size > 0) - newarg = sdscatlen(curarg,arg,size); - break; - case 'b': - arg = va_arg(ap,char*); - size = va_arg(ap,size_t); - if (size > 0) - newarg = sdscatlen(curarg,arg,size); - break; - case '%': - newarg = sdscat(curarg,"%"); - break; - default: - /* Try to detect printf format */ - { - static const char intfmts[] = "diouxX"; - static const char flags[] = "#0-+ "; - char _format[16]; - const char *_p = c+1; - size_t _l = 0; - va_list _cpy; - - /* Flags */ - while (*_p != '\0' && strchr(flags,*_p) != NULL) _p++; - - /* Field width */ - while (*_p != '\0' && isdigit(*_p)) _p++; - - /* Precision */ - if (*_p == '.') { - _p++; - while (*_p != '\0' && isdigit(*_p)) _p++; - } - - /* Copy va_list before consuming with va_arg */ - va_copy(_cpy,ap); - - /* Integer conversion (without modifiers) */ - if (strchr(intfmts,*_p) != NULL) { - va_arg(ap,int); - goto fmt_valid; - } - - /* Double conversion (without modifiers) */ - if (strchr("eEfFgGaA",*_p) != NULL) { - va_arg(ap,double); - goto fmt_valid; - } - - /* Size: char */ - if (_p[0] == 'h' && _p[1] == 'h') { - _p += 2; - if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { - va_arg(ap,int); /* char gets promoted to int */ - goto fmt_valid; - } - goto fmt_invalid; - } - - /* Size: short */ - if (_p[0] == 'h') { - _p += 1; - if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { - va_arg(ap,int); /* short gets promoted to int */ - goto fmt_valid; - } - goto fmt_invalid; - } - - /* Size: long long */ - if (_p[0] == 'l' && _p[1] == 'l') { - _p += 2; - if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { - va_arg(ap,long long); - goto fmt_valid; - } - goto fmt_invalid; - } - - /* Size: long */ - if (_p[0] == 'l') { - _p += 1; - if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { - va_arg(ap,long); - goto fmt_valid; - } - goto fmt_invalid; - } - - fmt_invalid: - va_end(_cpy); - goto format_err; - - fmt_valid: - _l = (_p+1)-c; - if (_l < sizeof(_format)-2) { - memcpy(_format,c,_l); - _format[_l] = '\0'; - newarg = sdscatvprintf(curarg,_format,_cpy); - - /* Update current position (note: outer blocks - * increment c twice so compensate here) */ - c = _p-1; - } - - va_end(_cpy); - break; - } - } - - if (newarg == NULL) goto memory_err; - curarg = newarg; - - touched = 1; - c++; - } - c++; - } - - /* Add the last argument if needed */ - if (touched) { - newargv = hi_realloc(curargv,sizeof(char*)*(argc+1)); - if (newargv == NULL) goto memory_err; - curargv = newargv; - curargv[argc++] = curarg; - totlen += bulklen(sdslen(curarg)); - } else { - sdsfree(curarg); - } - - /* Clear curarg because it was put in curargv or was free'd. */ - curarg = NULL; - - /* Add bytes needed to hold multi bulk count */ - totlen += 1+countDigits(argc)+2; - - /* Build the command at protocol level */ - cmd = hi_malloc(totlen+1); - if (cmd == NULL) goto memory_err; - - pos = sprintf(cmd,"*%d\r\n",argc); - for (j = 0; j < argc; j++) { - pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j])); - memcpy(cmd+pos,curargv[j],sdslen(curargv[j])); - pos += sdslen(curargv[j]); - sdsfree(curargv[j]); - cmd[pos++] = '\r'; - cmd[pos++] = '\n'; - } - assert(pos == totlen); - cmd[pos] = '\0'; - - hi_free(curargv); - *target = cmd; - return totlen; - -format_err: - error_type = -2; - goto cleanup; - -memory_err: - error_type = -1; - goto cleanup; - -cleanup: - if (curargv) { - while(argc--) - sdsfree(curargv[argc]); - hi_free(curargv); - } - - sdsfree(curarg); - hi_free(cmd); - - return error_type; -} - -/* Format a command according to the Redis protocol. This function - * takes a format similar to printf: - * - * %s represents a C null terminated string you want to interpolate - * %b represents a binary safe string - * - * When using %b you need to provide both the pointer to the string - * and the length in bytes as a size_t. Examples: - * - * len = redisFormatCommand(target, "GET %s", mykey); - * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen); - */ -int redisFormatCommand(char **target, const char *format, ...) { - va_list ap; - int len; - va_start(ap,format); - len = redisvFormatCommand(target,format,ap); - va_end(ap); - - /* The API says "-1" means bad result, but we now also return "-2" in some - * cases. Force the return value to always be -1. */ - if (len < 0) - len = -1; - - return len; -} - -/* Format a command according to the Redis protocol using an sds string and - * sdscatfmt for the processing of arguments. This function takes the - * number of arguments, an array with arguments and an array with their - * lengths. If the latter is set to NULL, strlen will be used to compute the - * argument lengths. - */ -int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv, - const size_t *argvlen) -{ - sds cmd, aux; - unsigned long long totlen; - int j; - size_t len; - - /* Abort on a NULL target */ - if (target == NULL) - return -1; - - /* Calculate our total size */ - totlen = 1+countDigits(argc)+2; - for (j = 0; j < argc; j++) { - len = argvlen ? argvlen[j] : strlen(argv[j]); - totlen += bulklen(len); - } - - /* Use an SDS string for command construction */ - cmd = sdsempty(); - if (cmd == NULL) - return -1; - - /* We already know how much storage we need */ - aux = sdsMakeRoomFor(cmd, totlen); - if (aux == NULL) { - sdsfree(cmd); - return -1; - } - - cmd = aux; - - /* Construct command */ - cmd = sdscatfmt(cmd, "*%i\r\n", argc); - for (j=0; j < argc; j++) { - len = argvlen ? argvlen[j] : strlen(argv[j]); - cmd = sdscatfmt(cmd, "$%u\r\n", len); - cmd = sdscatlen(cmd, argv[j], len); - cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1); - } - - assert(sdslen(cmd)==totlen); - - *target = cmd; - return totlen; -} - -void redisFreeSdsCommand(sds cmd) { - sdsfree(cmd); -} - -/* Format a command according to the Redis protocol. This function takes the - * number of arguments, an array with arguments and an array with their - * lengths. If the latter is set to NULL, strlen will be used to compute the - * argument lengths. - */ -int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) { - char *cmd = NULL; /* final command */ - int pos; /* position in final command */ - size_t len; - int totlen, j; - - /* Abort on a NULL target */ - if (target == NULL) - return -1; - - /* Calculate number of bytes needed for the command */ - totlen = 1+countDigits(argc)+2; - for (j = 0; j < argc; j++) { - len = argvlen ? argvlen[j] : strlen(argv[j]); - totlen += bulklen(len); - } - - /* Build the command at protocol level */ - cmd = hi_malloc(totlen+1); - if (cmd == NULL) - return -1; - - pos = sprintf(cmd,"*%d\r\n",argc); - for (j = 0; j < argc; j++) { - len = argvlen ? argvlen[j] : strlen(argv[j]); - pos += sprintf(cmd+pos,"$%zu\r\n",len); - memcpy(cmd+pos,argv[j],len); - pos += len; - cmd[pos++] = '\r'; - cmd[pos++] = '\n'; - } - assert(pos == totlen); - cmd[pos] = '\0'; - - *target = cmd; - return totlen; -} - -void redisFreeCommand(char *cmd) { - hi_free(cmd); -} - -void __redisSetError(redisContext *c, int type, const char *str) { - size_t len; - - c->err = type; - if (str != NULL) { - len = strlen(str); - len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1); - memcpy(c->errstr,str,len); - c->errstr[len] = '\0'; - } else { - /* Only REDIS_ERR_IO may lack a description! */ - assert(type == REDIS_ERR_IO); - strerror_r(errno, c->errstr, sizeof(c->errstr)); - } -} - -redisReader *redisReaderCreate(void) { - return redisReaderCreateWithFunctions(&defaultFunctions); -} - -static void redisPushAutoFree(void *privdata, void *reply) { - (void)privdata; - freeReplyObject(reply); -} - -static redisContext *redisContextInit(void) { - redisContext *c; - - c = hi_calloc(1, sizeof(*c)); - if (c == NULL) - return NULL; - - c->funcs = &redisContextDefaultFuncs; - - c->obuf = sdsempty(); - c->reader = redisReaderCreate(); - c->fd = REDIS_INVALID_FD; - - if (c->obuf == NULL || c->reader == NULL) { - redisFree(c); - return NULL; - } - - return c; -} - -void redisFree(redisContext *c) { - if (c == NULL) - return; - redisNetClose(c); - - sdsfree(c->obuf); - redisReaderFree(c->reader); - hi_free(c->tcp.host); - hi_free(c->tcp.source_addr); - hi_free(c->unix_sock.path); - hi_free(c->connect_timeout); - hi_free(c->command_timeout); - hi_free(c->saddr); - - if (c->privdata && c->free_privdata) - c->free_privdata(c->privdata); - - if (c->funcs->free_privctx) - c->funcs->free_privctx(c->privctx); - - memset(c, 0xff, sizeof(*c)); - hi_free(c); -} - -redisFD redisFreeKeepFd(redisContext *c) { - redisFD fd = c->fd; - c->fd = REDIS_INVALID_FD; - redisFree(c); - return fd; -} - -int redisReconnect(redisContext *c) { - c->err = 0; - memset(c->errstr, '\0', strlen(c->errstr)); - - if (c->privctx && c->funcs->free_privctx) { - c->funcs->free_privctx(c->privctx); - c->privctx = NULL; - } - - redisNetClose(c); - - sdsfree(c->obuf); - redisReaderFree(c->reader); - - c->obuf = sdsempty(); - c->reader = redisReaderCreate(); - - if (c->obuf == NULL || c->reader == NULL) { - __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); - return REDIS_ERR; - } - - int ret = REDIS_ERR; - if (c->connection_type == REDIS_CONN_TCP) { - ret = redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port, - c->connect_timeout, c->tcp.source_addr); - } else if (c->connection_type == REDIS_CONN_UNIX) { - ret = redisContextConnectUnix(c, c->unix_sock.path, c->connect_timeout); - } else { - /* Something bad happened here and shouldn't have. There isn't - enough information in the context to reconnect. */ - __redisSetError(c,REDIS_ERR_OTHER,"Not enough information to reconnect"); - ret = REDIS_ERR; - } - - if (c->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) { - redisContextSetTimeout(c, *c->command_timeout); - } - - return ret; -} - -redisContext *redisConnectWithOptions(const redisOptions *options) { - redisContext *c = redisContextInit(); - if (c == NULL) { - return NULL; - } - if (!(options->options & REDIS_OPT_NONBLOCK)) { - c->flags |= REDIS_BLOCK; - } - if (options->options & REDIS_OPT_REUSEADDR) { - c->flags |= REDIS_REUSEADDR; - } - if (options->options & REDIS_OPT_NOAUTOFREE) { - c->flags |= REDIS_NO_AUTO_FREE; - } - - /* Set any user supplied RESP3 PUSH handler or use freeReplyObject - * as a default unless specifically flagged that we don't want one. */ - if (options->push_cb != NULL) - redisSetPushCallback(c, options->push_cb); - else if (!(options->options & REDIS_OPT_NO_PUSH_AUTOFREE)) - redisSetPushCallback(c, redisPushAutoFree); - - c->privdata = options->privdata; - c->free_privdata = options->free_privdata; - - if (redisContextUpdateConnectTimeout(c, options->connect_timeout) != REDIS_OK || - redisContextUpdateCommandTimeout(c, options->command_timeout) != REDIS_OK) { - __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); - return c; - } - - if (options->type == REDIS_CONN_TCP) { - redisContextConnectBindTcp(c, options->endpoint.tcp.ip, - options->endpoint.tcp.port, options->connect_timeout, - options->endpoint.tcp.source_addr); - } else if (options->type == REDIS_CONN_UNIX) { - redisContextConnectUnix(c, options->endpoint.unix_socket, - options->connect_timeout); - } else if (options->type == REDIS_CONN_USERFD) { - c->fd = options->endpoint.fd; - c->flags |= REDIS_CONNECTED; - } else { - // Unknown type - FIXME - FREE - return NULL; - } - - if (options->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) { - redisContextSetTimeout(c, *options->command_timeout); - } - - return c; -} - -/* Connect to a Redis instance. On error the field error in the returned - * context will be set to the return value of the error function. - * When no set of reply functions is given, the default set will be used. */ -redisContext *redisConnect(const char *ip, int port) { - redisOptions options = {0}; - REDIS_OPTIONS_SET_TCP(&options, ip, port); - return redisConnectWithOptions(&options); -} - -redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) { - redisOptions options = {0}; - REDIS_OPTIONS_SET_TCP(&options, ip, port); - options.connect_timeout = &tv; - return redisConnectWithOptions(&options); -} - -redisContext *redisConnectNonBlock(const char *ip, int port) { - redisOptions options = {0}; - REDIS_OPTIONS_SET_TCP(&options, ip, port); - options.options |= REDIS_OPT_NONBLOCK; - return redisConnectWithOptions(&options); -} - -redisContext *redisConnectBindNonBlock(const char *ip, int port, - const char *source_addr) { - redisOptions options = {0}; - REDIS_OPTIONS_SET_TCP(&options, ip, port); - options.endpoint.tcp.source_addr = source_addr; - options.options |= REDIS_OPT_NONBLOCK; - return redisConnectWithOptions(&options); -} - -redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, - const char *source_addr) { - redisOptions options = {0}; - REDIS_OPTIONS_SET_TCP(&options, ip, port); - options.endpoint.tcp.source_addr = source_addr; - options.options |= REDIS_OPT_NONBLOCK|REDIS_OPT_REUSEADDR; - return redisConnectWithOptions(&options); -} - -redisContext *redisConnectUnix(const char *path) { - redisOptions options = {0}; - REDIS_OPTIONS_SET_UNIX(&options, path); - return redisConnectWithOptions(&options); -} - -redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) { - redisOptions options = {0}; - REDIS_OPTIONS_SET_UNIX(&options, path); - options.connect_timeout = &tv; - return redisConnectWithOptions(&options); -} - -redisContext *redisConnectUnixNonBlock(const char *path) { - redisOptions options = {0}; - REDIS_OPTIONS_SET_UNIX(&options, path); - options.options |= REDIS_OPT_NONBLOCK; - return redisConnectWithOptions(&options); -} - -redisContext *redisConnectFd(redisFD fd) { - redisOptions options = {0}; - options.type = REDIS_CONN_USERFD; - options.endpoint.fd = fd; - return redisConnectWithOptions(&options); -} - -/* Set read/write timeout on a blocking socket. */ -int redisSetTimeout(redisContext *c, const struct timeval tv) { - if (c->flags & REDIS_BLOCK) - return redisContextSetTimeout(c,tv); - return REDIS_ERR; -} - -/* Enable connection KeepAlive. */ -int redisEnableKeepAlive(redisContext *c) { - if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK) - return REDIS_ERR; - return REDIS_OK; -} - -/* Set a user provided RESP3 PUSH handler and return any old one set. */ -redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn) { - redisPushFn *old = c->push_cb; - c->push_cb = fn; - return old; -} - -/* Use this function to handle a read event on the descriptor. It will try - * and read some bytes from the socket and feed them to the reply parser. - * - * After this function is called, you may use redisGetReplyFromReader to - * see if there is a reply available. */ -int redisBufferRead(redisContext *c) { - char buf[1024*16]; - int nread; - - /* Return early when the context has seen an error. */ - if (c->err) - return REDIS_ERR; - - nread = c->funcs->read(c, buf, sizeof(buf)); - if (nread > 0) { - if (redisReaderFeed(c->reader, buf, nread) != REDIS_OK) { - __redisSetError(c, c->reader->err, c->reader->errstr); - return REDIS_ERR; - } else { - } - } else if (nread < 0) { - return REDIS_ERR; - } - return REDIS_OK; -} - -/* Write the output buffer to the socket. - * - * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was - * successfully written to the socket. When the buffer is empty after the - * write operation, "done" is set to 1 (if given). - * - * Returns REDIS_ERR if an error occurred trying to write and sets - * c->errstr to hold the appropriate error string. - */ -int redisBufferWrite(redisContext *c, int *done) { - - /* Return early when the context has seen an error. */ - if (c->err) - return REDIS_ERR; - - if (sdslen(c->obuf) > 0) { - ssize_t nwritten = c->funcs->write(c); - if (nwritten < 0) { - return REDIS_ERR; - } else if (nwritten > 0) { - if (nwritten == (ssize_t)sdslen(c->obuf)) { - sdsfree(c->obuf); - c->obuf = sdsempty(); - if (c->obuf == NULL) - goto oom; - } else { - if (sdsrange(c->obuf,nwritten,-1) < 0) goto oom; - } - } - } - if (done != NULL) *done = (sdslen(c->obuf) == 0); - return REDIS_OK; - -oom: - __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); - return REDIS_ERR; -} - -/* Internal helper function to try and get a reply from the reader, - * or set an error in the context otherwise. */ -int redisGetReplyFromReader(redisContext *c, void **reply) { - if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) { - __redisSetError(c,c->reader->err,c->reader->errstr); - return REDIS_ERR; - } - - return REDIS_OK; -} - -/* Internal helper that returns 1 if the reply was a RESP3 PUSH - * message and we handled it with a user-provided callback. */ -static int redisHandledPushReply(redisContext *c, void *reply) { - if (reply && c->push_cb && redisIsPushReply(reply)) { - c->push_cb(c->privdata, reply); - return 1; - } - - return 0; -} - -int redisGetReply(redisContext *c, void **reply) { - int wdone = 0; - void *aux = NULL; - - /* Try to read pending replies */ - if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) - return REDIS_ERR; - - /* For the blocking context, flush output buffer and read reply */ - if (aux == NULL && c->flags & REDIS_BLOCK) { - /* Write until done */ - do { - if (redisBufferWrite(c,&wdone) == REDIS_ERR) - return REDIS_ERR; - } while (!wdone); - - /* Read until there is a reply */ - do { - if (redisBufferRead(c) == REDIS_ERR) - return REDIS_ERR; - - /* We loop here in case the user has specified a RESP3 - * PUSH handler (e.g. for client tracking). */ - do { - if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) - return REDIS_ERR; - } while (redisHandledPushReply(c, aux)); - } while (aux == NULL); - } - - /* Set reply or free it if we were passed NULL */ - if (reply != NULL) { - *reply = aux; - } else { - freeReplyObject(aux); - } - - return REDIS_OK; -} - - -/* Helper function for the redisAppendCommand* family of functions. - * - * Write a formatted command to the output buffer. When this family - * is used, you need to call redisGetReply yourself to retrieve - * the reply (or replies in pub/sub). - */ -int __redisAppendCommand(redisContext *c, const char *cmd, size_t len) { - sds newbuf; - - newbuf = sdscatlen(c->obuf,cmd,len); - if (newbuf == NULL) { - __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); - return REDIS_ERR; - } - - c->obuf = newbuf; - return REDIS_OK; -} - -int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len) { - - if (__redisAppendCommand(c, cmd, len) != REDIS_OK) { - return REDIS_ERR; - } - - return REDIS_OK; -} - -int redisvAppendCommand(redisContext *c, const char *format, va_list ap) { - char *cmd; - int len; - - len = redisvFormatCommand(&cmd,format,ap); - if (len == -1) { - __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); - return REDIS_ERR; - } else if (len == -2) { - __redisSetError(c,REDIS_ERR_OTHER,"Invalid format string"); - return REDIS_ERR; - } - - if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { - hi_free(cmd); - return REDIS_ERR; - } - - hi_free(cmd); - return REDIS_OK; -} - -int redisAppendCommand(redisContext *c, const char *format, ...) { - va_list ap; - int ret; - - va_start(ap,format); - ret = redisvAppendCommand(c,format,ap); - va_end(ap); - return ret; -} - -int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { - sds cmd; - int len; - - len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen); - if (len == -1) { - __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); - return REDIS_ERR; - } - - if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { - sdsfree(cmd); - return REDIS_ERR; - } - - sdsfree(cmd); - return REDIS_OK; -} - -/* Helper function for the redisCommand* family of functions. - * - * Write a formatted command to the output buffer. If the given context is - * blocking, immediately read the reply into the "reply" pointer. When the - * context is non-blocking, the "reply" pointer will not be used and the - * command is simply appended to the write buffer. - * - * Returns the reply when a reply was successfully retrieved. Returns NULL - * otherwise. When NULL is returned in a blocking context, the error field - * in the context will be set. - */ -static void *__redisBlockForReply(redisContext *c) { - void *reply; - - if (c->flags & REDIS_BLOCK) { - if (redisGetReply(c,&reply) != REDIS_OK) - return NULL; - return reply; - } - return NULL; -} - -void *redisvCommand(redisContext *c, const char *format, va_list ap) { - if (redisvAppendCommand(c,format,ap) != REDIS_OK) - return NULL; - return __redisBlockForReply(c); -} - -void *redisCommand(redisContext *c, const char *format, ...) { - va_list ap; - va_start(ap,format); - void *reply = redisvCommand(c,format,ap); - va_end(ap); - return reply; -} - -void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { - if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK) - return NULL; - return __redisBlockForReply(c); -} diff --git a/ext/hiredis-1.0.2/hiredis.h b/ext/hiredis-1.0.2/hiredis.h deleted file mode 100644 index 3bc46d992..000000000 --- a/ext/hiredis-1.0.2/hiredis.h +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2014, Pieter Noordhuis - * Copyright (c) 2015, Matt Stancliff , - * Jan-Erik Rediger - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_H -#define __HIREDIS_H -#include "read.h" -#include /* for va_list */ -#ifndef _MSC_VER -#include /* for struct timeval */ -#else -struct timeval; /* forward declaration */ -typedef long long ssize_t; -#endif -#include /* uintXX_t, etc */ -#include "sds.h" /* for sds */ -#include "alloc.h" /* for allocation wrappers */ - -#define HIREDIS_MAJOR 1 -#define HIREDIS_MINOR 0 -#define HIREDIS_PATCH 2 -#define HIREDIS_SONAME 1.0.0 - -/* Connection type can be blocking or non-blocking and is set in the - * least significant bit of the flags field in redisContext. */ -#define REDIS_BLOCK 0x1 - -/* Connection may be disconnected before being free'd. The second bit - * in the flags field is set when the context is connected. */ -#define REDIS_CONNECTED 0x2 - -/* The async API might try to disconnect cleanly and flush the output - * buffer and read all subsequent replies before disconnecting. - * This flag means no new commands can come in and the connection - * should be terminated once all replies have been read. */ -#define REDIS_DISCONNECTING 0x4 - -/* Flag specific to the async API which means that the context should be clean - * up as soon as possible. */ -#define REDIS_FREEING 0x8 - -/* Flag that is set when an async callback is executed. */ -#define REDIS_IN_CALLBACK 0x10 - -/* Flag that is set when the async context has one or more subscriptions. */ -#define REDIS_SUBSCRIBED 0x20 - -/* Flag that is set when monitor mode is active */ -#define REDIS_MONITORING 0x40 - -/* Flag that is set when we should set SO_REUSEADDR before calling bind() */ -#define REDIS_REUSEADDR 0x80 - -/** - * Flag that indicates the user does not want the context to - * be automatically freed upon error - */ -#define REDIS_NO_AUTO_FREE 0x200 - -#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */ - -/* number of times we retry to connect in the case of EADDRNOTAVAIL and - * SO_REUSEADDR is being used. */ -#define REDIS_CONNECT_RETRIES 10 - -/* Forward declarations for structs defined elsewhere */ -struct redisAsyncContext; -struct redisContext; - -/* RESP3 push helpers and callback prototypes */ -#define redisIsPushReply(r) (((redisReply*)(r))->type == REDIS_REPLY_PUSH) -typedef void (redisPushFn)(void *, void *); -typedef void (redisAsyncPushFn)(struct redisAsyncContext *, void *); - -#ifdef __cplusplus -extern "C" { -#endif - -/* This is the reply object returned by redisCommand() */ -typedef struct redisReply { - int type; /* REDIS_REPLY_* */ - long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ - double dval; /* The double when type is REDIS_REPLY_DOUBLE */ - size_t len; /* Length of string */ - char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING - REDIS_REPLY_VERB, and REDIS_REPLY_DOUBLE (in additional to dval). */ - char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null - terminated 3 character content type, such as "txt". */ - size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ - struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ -} redisReply; - -redisReader *redisReaderCreate(void); - -/* Function to free the reply objects hiredis returns by default. */ -void freeReplyObject(void *reply); - -/* Functions to format a command according to the protocol. */ -int redisvFormatCommand(char **target, const char *format, va_list ap); -int redisFormatCommand(char **target, const char *format, ...); -int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen); -int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen); -void redisFreeCommand(char *cmd); -void redisFreeSdsCommand(sds cmd); - -enum redisConnectionType { - REDIS_CONN_TCP, - REDIS_CONN_UNIX, - REDIS_CONN_USERFD -}; - -struct redisSsl; - -#define REDIS_OPT_NONBLOCK 0x01 -#define REDIS_OPT_REUSEADDR 0x02 - -/** - * Don't automatically free the async object on a connection failure, - * or other implicit conditions. Only free on an explicit call to disconnect() or free() - */ -#define REDIS_OPT_NOAUTOFREE 0x04 - -/* Don't automatically intercept and free RESP3 PUSH replies. */ -#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08 - -/* In Unix systems a file descriptor is a regular signed int, with -1 - * representing an invalid descriptor. In Windows it is a SOCKET - * (32- or 64-bit unsigned integer depending on the architecture), where - * all bits set (~0) is INVALID_SOCKET. */ -#ifndef _WIN32 -typedef int redisFD; -#define REDIS_INVALID_FD -1 -#else -#ifdef _WIN64 -typedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */ -#else -typedef unsigned long redisFD; /* SOCKET = 32-bit UINT_PTR */ -#endif -#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */ -#endif - -typedef struct { - /* - * the type of connection to use. This also indicates which - * `endpoint` member field to use - */ - int type; - /* bit field of REDIS_OPT_xxx */ - int options; - /* timeout value for connect operation. If NULL, no timeout is used */ - const struct timeval *connect_timeout; - /* timeout value for commands. If NULL, no timeout is used. This can be - * updated at runtime with redisSetTimeout/redisAsyncSetTimeout. */ - const struct timeval *command_timeout; - union { - /** use this field for tcp/ip connections */ - struct { - const char *source_addr; - const char *ip; - int port; - } tcp; - /** use this field for unix domain sockets */ - const char *unix_socket; - /** - * use this field to have hiredis operate an already-open - * file descriptor */ - redisFD fd; - } endpoint; - - /* Optional user defined data/destructor */ - void *privdata; - void (*free_privdata)(void *); - - /* A user defined PUSH message callback */ - redisPushFn *push_cb; - redisAsyncPushFn *async_push_cb; -} redisOptions; - -/** - * Helper macros to initialize options to their specified fields. - */ -#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \ - (opts)->type = REDIS_CONN_TCP; \ - (opts)->endpoint.tcp.ip = ip_; \ - (opts)->endpoint.tcp.port = port_; - -#define REDIS_OPTIONS_SET_UNIX(opts, path) \ - (opts)->type = REDIS_CONN_UNIX; \ - (opts)->endpoint.unix_socket = path; - -#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \ - (opts)->privdata = data; \ - (opts)->free_privdata = dtor; \ - -typedef struct redisContextFuncs { - void (*free_privctx)(void *); - void (*async_read)(struct redisAsyncContext *); - void (*async_write)(struct redisAsyncContext *); - ssize_t (*read)(struct redisContext *, char *, size_t); - ssize_t (*write)(struct redisContext *); -} redisContextFuncs; - -/* Context for a connection to Redis */ -typedef struct redisContext { - const redisContextFuncs *funcs; /* Function table */ - - int err; /* Error flags, 0 when there is no error */ - char errstr[128]; /* String representation of error when applicable */ - redisFD fd; - int flags; - char *obuf; /* Write buffer */ - redisReader *reader; /* Protocol reader */ - - enum redisConnectionType connection_type; - struct timeval *connect_timeout; - struct timeval *command_timeout; - - struct { - char *host; - char *source_addr; - int port; - } tcp; - - struct { - char *path; - } unix_sock; - - /* For non-blocking connect */ - struct sockadr *saddr; - size_t addrlen; - - /* Optional data and corresponding destructor users can use to provide - * context to a given redisContext. Not used by hiredis. */ - void *privdata; - void (*free_privdata)(void *); - - /* Internal context pointer presently used by hiredis to manage - * SSL connections. */ - void *privctx; - - /* An optional RESP3 PUSH handler */ - redisPushFn *push_cb; -} redisContext; - -redisContext *redisConnectWithOptions(const redisOptions *options); -redisContext *redisConnect(const char *ip, int port); -redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); -redisContext *redisConnectNonBlock(const char *ip, int port); -redisContext *redisConnectBindNonBlock(const char *ip, int port, - const char *source_addr); -redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, - const char *source_addr); -redisContext *redisConnectUnix(const char *path); -redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); -redisContext *redisConnectUnixNonBlock(const char *path); -redisContext *redisConnectFd(redisFD fd); - -/** - * Reconnect the given context using the saved information. - * - * This re-uses the exact same connect options as in the initial connection. - * host, ip (or path), timeout and bind address are reused, - * flags are used unmodified from the existing context. - * - * Returns REDIS_OK on successful connect or REDIS_ERR otherwise. - */ -int redisReconnect(redisContext *c); - -redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn); -int redisSetTimeout(redisContext *c, const struct timeval tv); -int redisEnableKeepAlive(redisContext *c); -void redisFree(redisContext *c); -redisFD redisFreeKeepFd(redisContext *c); -int redisBufferRead(redisContext *c); -int redisBufferWrite(redisContext *c, int *done); - -/* In a blocking context, this function first checks if there are unconsumed - * replies to return and returns one if so. Otherwise, it flushes the output - * buffer to the socket and reads until it has a reply. In a non-blocking - * context, it will return unconsumed replies until there are no more. */ -int redisGetReply(redisContext *c, void **reply); -int redisGetReplyFromReader(redisContext *c, void **reply); - -/* Write a formatted command to the output buffer. Use these functions in blocking mode - * to get a pipeline of commands. */ -int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len); - -/* Write a command to the output buffer. Use these functions in blocking mode - * to get a pipeline of commands. */ -int redisvAppendCommand(redisContext *c, const char *format, va_list ap); -int redisAppendCommand(redisContext *c, const char *format, ...); -int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); - -/* Issue a command to Redis. In a blocking context, it is identical to calling - * redisAppendCommand, followed by redisGetReply. The function will return - * NULL if there was an error in performing the request, otherwise it will - * return the reply. In a non-blocking context, it is identical to calling - * only redisAppendCommand and will always return NULL. */ -void *redisvCommand(redisContext *c, const char *format, va_list ap); -void *redisCommand(redisContext *c, const char *format, ...); -void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/ext/hiredis-1.0.2/hiredis.pc.in b/ext/hiredis-1.0.2/hiredis.pc.in deleted file mode 100644 index 91b773183..000000000 --- a/ext/hiredis-1.0.2/hiredis.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@CMAKE_INSTALL_PREFIX@ -install_libdir=@CMAKE_INSTALL_LIBDIR@ -exec_prefix=${prefix} -libdir=${exec_prefix}/${install_libdir} -includedir=${prefix}/include -pkgincludedir=${includedir}/hiredis - -Name: hiredis -Description: Minimalistic C client library for Redis. -Version: @PROJECT_VERSION@ -Libs: -L${libdir} -lhiredis -Cflags: -I${pkgincludedir} -D_FILE_OFFSET_BITS=64 diff --git a/ext/hiredis-1.0.2/hiredis_ssl-config.cmake.in b/ext/hiredis-1.0.2/hiredis_ssl-config.cmake.in deleted file mode 100644 index 9a283dfc2..000000000 --- a/ext/hiredis-1.0.2/hiredis_ssl-config.cmake.in +++ /dev/null @@ -1,13 +0,0 @@ -@PACKAGE_INIT@ - -set_and_check(hiredis_ssl_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@") - -IF (NOT TARGET hiredis::hiredis_ssl) - INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis_ssl-targets.cmake) -ENDIF() - -SET(hiredis_ssl_LIBRARIES hiredis::hiredis_ssl) -SET(hiredis_ssl_INCLUDE_DIRS ${hiredis_ssl_INCLUDEDIR}) - -check_required_components(hiredis_ssl) - diff --git a/ext/hiredis-1.0.2/hiredis_ssl.h b/ext/hiredis-1.0.2/hiredis_ssl.h deleted file mode 100644 index 604efe0c1..000000000 --- a/ext/hiredis-1.0.2/hiredis_ssl.h +++ /dev/null @@ -1,127 +0,0 @@ - -/* - * Copyright (c) 2019, Redis Labs - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_SSL_H -#define __HIREDIS_SSL_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* This is the underlying struct for SSL in ssl.h, which is not included to - * keep build dependencies short here. - */ -struct ssl_st; - -/* A wrapper around OpenSSL SSL_CTX to allow easy SSL use without directly - * calling OpenSSL. - */ -typedef struct redisSSLContext redisSSLContext; - -/** - * Initialization errors that redisCreateSSLContext() may return. - */ - -typedef enum { - REDIS_SSL_CTX_NONE = 0, /* No Error */ - REDIS_SSL_CTX_CREATE_FAILED, /* Failed to create OpenSSL SSL_CTX */ - REDIS_SSL_CTX_CERT_KEY_REQUIRED, /* Client cert and key must both be specified or skipped */ - REDIS_SSL_CTX_CA_CERT_LOAD_FAILED, /* Failed to load CA Certificate or CA Path */ - REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED, /* Failed to load client certificate */ - REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED /* Failed to load private key */ -} redisSSLContextError; - -/** - * Return the error message corresponding with the specified error code. - */ - -const char *redisSSLContextGetError(redisSSLContextError error); - -/** - * Helper function to initialize the OpenSSL library. - * - * OpenSSL requires one-time initialization before it can be used. Callers should - * call this function only once, and only if OpenSSL is not directly initialized - * elsewhere. - */ -int redisInitOpenSSL(void); - -/** - * Helper function to initialize an OpenSSL context that can be used - * to initiate SSL connections. - * - * cacert_filename is an optional name of a CA certificate/bundle file to load - * and use for validation. - * - * capath is an optional directory path where trusted CA certificate files are - * stored in an OpenSSL-compatible structure. - * - * cert_filename and private_key_filename are optional names of a client side - * certificate and private key files to use for authentication. They need to - * be both specified or omitted. - * - * server_name is an optional and will be used as a server name indication - * (SNI) TLS extension. - * - * If error is non-null, it will be populated in case the context creation fails - * (returning a NULL). - */ - -redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath, - const char *cert_filename, const char *private_key_filename, - const char *server_name, redisSSLContextError *error); - -/** - * Free a previously created OpenSSL context. - */ -void redisFreeSSLContext(redisSSLContext *redis_ssl_ctx); - -/** - * Initiate SSL on an existing redisContext. - * - * This is similar to redisInitiateSSL() but does not require the caller - * to directly interact with OpenSSL, and instead uses a redisSSLContext - * previously created using redisCreateSSLContext(). - */ - -int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx); - -/** - * Initiate SSL/TLS negotiation on a provided OpenSSL SSL object. - */ - -int redisInitiateSSL(redisContext *c, struct ssl_st *ssl); - -#ifdef __cplusplus -} -#endif - -#endif /* __HIREDIS_SSL_H */ diff --git a/ext/hiredis-1.0.2/hiredis_ssl.pc.in b/ext/hiredis-1.0.2/hiredis_ssl.pc.in deleted file mode 100644 index 588a978a5..000000000 --- a/ext/hiredis-1.0.2/hiredis_ssl.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@CMAKE_INSTALL_PREFIX@ -exec_prefix=${prefix} -libdir=${exec_prefix}/lib -includedir=${prefix}/include -pkgincludedir=${includedir}/hiredis - -Name: hiredis_ssl -Description: SSL Support for hiredis. -Version: @PROJECT_VERSION@ -Requires: hiredis -Libs: -L${libdir} -lhiredis_ssl -Libs.private: -lssl -lcrypto diff --git a/ext/hiredis-1.0.2/include/hiredis/alloc.h b/ext/hiredis-1.0.2/include/hiredis/alloc.h deleted file mode 100644 index 34a05f49f..000000000 --- a/ext/hiredis-1.0.2/include/hiredis/alloc.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2020, Michael Grunder - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef HIREDIS_ALLOC_H -#define HIREDIS_ALLOC_H - -#include /* for size_t */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* Structure pointing to our actually configured allocators */ -typedef struct hiredisAllocFuncs { - void *(*mallocFn)(size_t); - void *(*callocFn)(size_t,size_t); - void *(*reallocFn)(void*,size_t); - char *(*strdupFn)(const char*); - void (*freeFn)(void*); -} hiredisAllocFuncs; - -hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha); -void hiredisResetAllocators(void); - -#ifndef _WIN32 - -/* Hiredis' configured allocator function pointer struct */ -extern hiredisAllocFuncs hiredisAllocFns; - -static inline void *hi_malloc(size_t size) { - return hiredisAllocFns.mallocFn(size); -} - -static inline void *hi_calloc(size_t nmemb, size_t size) { - return hiredisAllocFns.callocFn(nmemb, size); -} - -static inline void *hi_realloc(void *ptr, size_t size) { - return hiredisAllocFns.reallocFn(ptr, size); -} - -static inline char *hi_strdup(const char *str) { - return hiredisAllocFns.strdupFn(str); -} - -static inline void hi_free(void *ptr) { - hiredisAllocFns.freeFn(ptr); -} - -#else - -void *hi_malloc(size_t size); -void *hi_calloc(size_t nmemb, size_t size); -void *hi_realloc(void *ptr, size_t size); -char *hi_strdup(const char *str); -void hi_free(void *ptr); - -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* HIREDIS_ALLOC_H */ diff --git a/ext/hiredis-1.0.2/include/hiredis/async.h b/ext/hiredis-1.0.2/include/hiredis/async.h deleted file mode 100644 index b1d2cb263..000000000 --- a/ext/hiredis-1.0.2/include/hiredis/async.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_ASYNC_H -#define __HIREDIS_ASYNC_H -#include "hiredis.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct redisAsyncContext; /* need forward declaration of redisAsyncContext */ -struct dict; /* dictionary header is included in async.c */ - -/* Reply callback prototype and container */ -typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*); -typedef struct redisCallback { - struct redisCallback *next; /* simple singly linked list */ - redisCallbackFn *fn; - int pending_subs; - void *privdata; -} redisCallback; - -/* List of callbacks for either regular replies or pub/sub */ -typedef struct redisCallbackList { - redisCallback *head, *tail; -} redisCallbackList; - -/* Connection callback prototypes */ -typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status); -typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status); -typedef void(redisTimerCallback)(void *timer, void *privdata); - -/* Context for an async connection to Redis */ -typedef struct redisAsyncContext { - /* Hold the regular context, so it can be realloc'ed. */ - redisContext c; - - /* Setup error flags so they can be used directly. */ - int err; - char *errstr; - - /* Not used by hiredis */ - void *data; - void (*dataCleanup)(void *privdata); - - /* Event library data and hooks */ - struct { - void *data; - - /* Hooks that are called when the library expects to start - * reading/writing. These functions should be idempotent. */ - void (*addRead)(void *privdata); - void (*delRead)(void *privdata); - void (*addWrite)(void *privdata); - void (*delWrite)(void *privdata); - void (*cleanup)(void *privdata); - void (*scheduleTimer)(void *privdata, struct timeval tv); - } ev; - - /* Called when either the connection is terminated due to an error or per - * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */ - redisDisconnectCallback *onDisconnect; - - /* Called when the first write event was received. */ - redisConnectCallback *onConnect; - - /* Regular command callbacks */ - redisCallbackList replies; - - /* Address used for connect() */ - struct sockaddr *saddr; - size_t addrlen; - - /* Subscription callbacks */ - struct { - redisCallbackList invalid; - struct dict *channels; - struct dict *patterns; - } sub; - - /* Any configured RESP3 PUSH handler */ - redisAsyncPushFn *push_cb; -} redisAsyncContext; - -/* Functions that proxy to hiredis */ -redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options); -redisAsyncContext *redisAsyncConnect(const char *ip, int port); -redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr); -redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, - const char *source_addr); -redisAsyncContext *redisAsyncConnectUnix(const char *path); -int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); -int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); - -redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn); -int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv); -void redisAsyncDisconnect(redisAsyncContext *ac); -void redisAsyncFree(redisAsyncContext *ac); - -/* Handle read/write events */ -void redisAsyncHandleRead(redisAsyncContext *ac); -void redisAsyncHandleWrite(redisAsyncContext *ac); -void redisAsyncHandleTimeout(redisAsyncContext *ac); -void redisAsyncRead(redisAsyncContext *ac); -void redisAsyncWrite(redisAsyncContext *ac); - -/* Command functions for an async context. Write the command to the - * output buffer and register the provided callback. */ -int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap); -int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...); -int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen); -int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/ext/hiredis-1.0.2/include/hiredis/async_private.h b/ext/hiredis-1.0.2/include/hiredis/async_private.h deleted file mode 100644 index b9d23fffd..000000000 --- a/ext/hiredis-1.0.2/include/hiredis/async_private.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_ASYNC_PRIVATE_H -#define __HIREDIS_ASYNC_PRIVATE_H - -#define _EL_ADD_READ(ctx) \ - do { \ - refreshTimeout(ctx); \ - if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \ - } while (0) -#define _EL_DEL_READ(ctx) do { \ - if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \ - } while(0) -#define _EL_ADD_WRITE(ctx) \ - do { \ - refreshTimeout(ctx); \ - if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \ - } while (0) -#define _EL_DEL_WRITE(ctx) do { \ - if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \ - } while(0) -#define _EL_CLEANUP(ctx) do { \ - if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \ - ctx->ev.cleanup = NULL; \ - } while(0); - -static inline void refreshTimeout(redisAsyncContext *ctx) { - #define REDIS_TIMER_ISSET(tvp) \ - (tvp && ((tvp)->tv_sec || (tvp)->tv_usec)) - - #define REDIS_EL_TIMER(ac, tvp) \ - if ((ac)->ev.scheduleTimer && REDIS_TIMER_ISSET(tvp)) { \ - (ac)->ev.scheduleTimer((ac)->ev.data, *(tvp)); \ - } - - if (ctx->c.flags & REDIS_CONNECTED) { - REDIS_EL_TIMER(ctx, ctx->c.command_timeout); - } else { - REDIS_EL_TIMER(ctx, ctx->c.connect_timeout); - } -} - -void __redisAsyncDisconnect(redisAsyncContext *ac); -void redisProcessCallbacks(redisAsyncContext *ac); - -#endif /* __HIREDIS_ASYNC_PRIVATE_H */ diff --git a/ext/hiredis-1.0.2/include/hiredis/dict.h b/ext/hiredis-1.0.2/include/hiredis/dict.h deleted file mode 100644 index 95fcd280e..000000000 --- a/ext/hiredis-1.0.2/include/hiredis/dict.h +++ /dev/null @@ -1,126 +0,0 @@ -/* Hash table implementation. - * - * This file implements in memory hash tables with insert/del/replace/find/ - * get-random-element operations. Hash tables will auto resize if needed - * tables of power of two in size are used, collisions are handled by - * chaining. See the source code for more information... :) - * - * Copyright (c) 2006-2010, Salvatore Sanfilippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __DICT_H -#define __DICT_H - -#define DICT_OK 0 -#define DICT_ERR 1 - -/* Unused arguments generate annoying warnings... */ -#define DICT_NOTUSED(V) ((void) V) - -typedef struct dictEntry { - void *key; - void *val; - struct dictEntry *next; -} dictEntry; - -typedef struct dictType { - unsigned int (*hashFunction)(const void *key); - void *(*keyDup)(void *privdata, const void *key); - void *(*valDup)(void *privdata, const void *obj); - int (*keyCompare)(void *privdata, const void *key1, const void *key2); - void (*keyDestructor)(void *privdata, void *key); - void (*valDestructor)(void *privdata, void *obj); -} dictType; - -typedef struct dict { - dictEntry **table; - dictType *type; - unsigned long size; - unsigned long sizemask; - unsigned long used; - void *privdata; -} dict; - -typedef struct dictIterator { - dict *ht; - int index; - dictEntry *entry, *nextEntry; -} dictIterator; - -/* This is the initial size of every hash table */ -#define DICT_HT_INITIAL_SIZE 4 - -/* ------------------------------- Macros ------------------------------------*/ -#define dictFreeEntryVal(ht, entry) \ - if ((ht)->type->valDestructor) \ - (ht)->type->valDestructor((ht)->privdata, (entry)->val) - -#define dictSetHashVal(ht, entry, _val_) do { \ - if ((ht)->type->valDup) \ - entry->val = (ht)->type->valDup((ht)->privdata, _val_); \ - else \ - entry->val = (_val_); \ -} while(0) - -#define dictFreeEntryKey(ht, entry) \ - if ((ht)->type->keyDestructor) \ - (ht)->type->keyDestructor((ht)->privdata, (entry)->key) - -#define dictSetHashKey(ht, entry, _key_) do { \ - if ((ht)->type->keyDup) \ - entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \ - else \ - entry->key = (_key_); \ -} while(0) - -#define dictCompareHashKeys(ht, key1, key2) \ - (((ht)->type->keyCompare) ? \ - (ht)->type->keyCompare((ht)->privdata, key1, key2) : \ - (key1) == (key2)) - -#define dictHashKey(ht, key) (ht)->type->hashFunction(key) - -#define dictGetEntryKey(he) ((he)->key) -#define dictGetEntryVal(he) ((he)->val) -#define dictSlots(ht) ((ht)->size) -#define dictSize(ht) ((ht)->used) - -/* API */ -static unsigned int dictGenHashFunction(const unsigned char *buf, int len); -static dict *dictCreate(dictType *type, void *privDataPtr); -static int dictExpand(dict *ht, unsigned long size); -static int dictAdd(dict *ht, void *key, void *val); -static int dictReplace(dict *ht, void *key, void *val); -static int dictDelete(dict *ht, const void *key); -static void dictRelease(dict *ht); -static dictEntry * dictFind(dict *ht, const void *key); -static dictIterator *dictGetIterator(dict *ht); -static dictEntry *dictNext(dictIterator *iter); -static void dictReleaseIterator(dictIterator *iter); - -#endif /* __DICT_H */ diff --git a/ext/hiredis-1.0.2/include/hiredis/fmacros.h b/ext/hiredis-1.0.2/include/hiredis/fmacros.h deleted file mode 100644 index 3227faafd..000000000 --- a/ext/hiredis-1.0.2/include/hiredis/fmacros.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __HIREDIS_FMACRO_H -#define __HIREDIS_FMACRO_H - -#define _XOPEN_SOURCE 600 -#define _POSIX_C_SOURCE 200112L - -#if defined(__APPLE__) && defined(__MACH__) -/* Enable TCP_KEEPALIVE */ -#define _DARWIN_C_SOURCE -#endif - -#endif diff --git a/ext/hiredis-1.0.2/include/hiredis/hiredis.h b/ext/hiredis-1.0.2/include/hiredis/hiredis.h deleted file mode 100644 index 3bc46d992..000000000 --- a/ext/hiredis-1.0.2/include/hiredis/hiredis.h +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2014, Pieter Noordhuis - * Copyright (c) 2015, Matt Stancliff , - * Jan-Erik Rediger - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_H -#define __HIREDIS_H -#include "read.h" -#include /* for va_list */ -#ifndef _MSC_VER -#include /* for struct timeval */ -#else -struct timeval; /* forward declaration */ -typedef long long ssize_t; -#endif -#include /* uintXX_t, etc */ -#include "sds.h" /* for sds */ -#include "alloc.h" /* for allocation wrappers */ - -#define HIREDIS_MAJOR 1 -#define HIREDIS_MINOR 0 -#define HIREDIS_PATCH 2 -#define HIREDIS_SONAME 1.0.0 - -/* Connection type can be blocking or non-blocking and is set in the - * least significant bit of the flags field in redisContext. */ -#define REDIS_BLOCK 0x1 - -/* Connection may be disconnected before being free'd. The second bit - * in the flags field is set when the context is connected. */ -#define REDIS_CONNECTED 0x2 - -/* The async API might try to disconnect cleanly and flush the output - * buffer and read all subsequent replies before disconnecting. - * This flag means no new commands can come in and the connection - * should be terminated once all replies have been read. */ -#define REDIS_DISCONNECTING 0x4 - -/* Flag specific to the async API which means that the context should be clean - * up as soon as possible. */ -#define REDIS_FREEING 0x8 - -/* Flag that is set when an async callback is executed. */ -#define REDIS_IN_CALLBACK 0x10 - -/* Flag that is set when the async context has one or more subscriptions. */ -#define REDIS_SUBSCRIBED 0x20 - -/* Flag that is set when monitor mode is active */ -#define REDIS_MONITORING 0x40 - -/* Flag that is set when we should set SO_REUSEADDR before calling bind() */ -#define REDIS_REUSEADDR 0x80 - -/** - * Flag that indicates the user does not want the context to - * be automatically freed upon error - */ -#define REDIS_NO_AUTO_FREE 0x200 - -#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */ - -/* number of times we retry to connect in the case of EADDRNOTAVAIL and - * SO_REUSEADDR is being used. */ -#define REDIS_CONNECT_RETRIES 10 - -/* Forward declarations for structs defined elsewhere */ -struct redisAsyncContext; -struct redisContext; - -/* RESP3 push helpers and callback prototypes */ -#define redisIsPushReply(r) (((redisReply*)(r))->type == REDIS_REPLY_PUSH) -typedef void (redisPushFn)(void *, void *); -typedef void (redisAsyncPushFn)(struct redisAsyncContext *, void *); - -#ifdef __cplusplus -extern "C" { -#endif - -/* This is the reply object returned by redisCommand() */ -typedef struct redisReply { - int type; /* REDIS_REPLY_* */ - long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ - double dval; /* The double when type is REDIS_REPLY_DOUBLE */ - size_t len; /* Length of string */ - char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING - REDIS_REPLY_VERB, and REDIS_REPLY_DOUBLE (in additional to dval). */ - char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null - terminated 3 character content type, such as "txt". */ - size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ - struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ -} redisReply; - -redisReader *redisReaderCreate(void); - -/* Function to free the reply objects hiredis returns by default. */ -void freeReplyObject(void *reply); - -/* Functions to format a command according to the protocol. */ -int redisvFormatCommand(char **target, const char *format, va_list ap); -int redisFormatCommand(char **target, const char *format, ...); -int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen); -int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen); -void redisFreeCommand(char *cmd); -void redisFreeSdsCommand(sds cmd); - -enum redisConnectionType { - REDIS_CONN_TCP, - REDIS_CONN_UNIX, - REDIS_CONN_USERFD -}; - -struct redisSsl; - -#define REDIS_OPT_NONBLOCK 0x01 -#define REDIS_OPT_REUSEADDR 0x02 - -/** - * Don't automatically free the async object on a connection failure, - * or other implicit conditions. Only free on an explicit call to disconnect() or free() - */ -#define REDIS_OPT_NOAUTOFREE 0x04 - -/* Don't automatically intercept and free RESP3 PUSH replies. */ -#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08 - -/* In Unix systems a file descriptor is a regular signed int, with -1 - * representing an invalid descriptor. In Windows it is a SOCKET - * (32- or 64-bit unsigned integer depending on the architecture), where - * all bits set (~0) is INVALID_SOCKET. */ -#ifndef _WIN32 -typedef int redisFD; -#define REDIS_INVALID_FD -1 -#else -#ifdef _WIN64 -typedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */ -#else -typedef unsigned long redisFD; /* SOCKET = 32-bit UINT_PTR */ -#endif -#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */ -#endif - -typedef struct { - /* - * the type of connection to use. This also indicates which - * `endpoint` member field to use - */ - int type; - /* bit field of REDIS_OPT_xxx */ - int options; - /* timeout value for connect operation. If NULL, no timeout is used */ - const struct timeval *connect_timeout; - /* timeout value for commands. If NULL, no timeout is used. This can be - * updated at runtime with redisSetTimeout/redisAsyncSetTimeout. */ - const struct timeval *command_timeout; - union { - /** use this field for tcp/ip connections */ - struct { - const char *source_addr; - const char *ip; - int port; - } tcp; - /** use this field for unix domain sockets */ - const char *unix_socket; - /** - * use this field to have hiredis operate an already-open - * file descriptor */ - redisFD fd; - } endpoint; - - /* Optional user defined data/destructor */ - void *privdata; - void (*free_privdata)(void *); - - /* A user defined PUSH message callback */ - redisPushFn *push_cb; - redisAsyncPushFn *async_push_cb; -} redisOptions; - -/** - * Helper macros to initialize options to their specified fields. - */ -#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \ - (opts)->type = REDIS_CONN_TCP; \ - (opts)->endpoint.tcp.ip = ip_; \ - (opts)->endpoint.tcp.port = port_; - -#define REDIS_OPTIONS_SET_UNIX(opts, path) \ - (opts)->type = REDIS_CONN_UNIX; \ - (opts)->endpoint.unix_socket = path; - -#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \ - (opts)->privdata = data; \ - (opts)->free_privdata = dtor; \ - -typedef struct redisContextFuncs { - void (*free_privctx)(void *); - void (*async_read)(struct redisAsyncContext *); - void (*async_write)(struct redisAsyncContext *); - ssize_t (*read)(struct redisContext *, char *, size_t); - ssize_t (*write)(struct redisContext *); -} redisContextFuncs; - -/* Context for a connection to Redis */ -typedef struct redisContext { - const redisContextFuncs *funcs; /* Function table */ - - int err; /* Error flags, 0 when there is no error */ - char errstr[128]; /* String representation of error when applicable */ - redisFD fd; - int flags; - char *obuf; /* Write buffer */ - redisReader *reader; /* Protocol reader */ - - enum redisConnectionType connection_type; - struct timeval *connect_timeout; - struct timeval *command_timeout; - - struct { - char *host; - char *source_addr; - int port; - } tcp; - - struct { - char *path; - } unix_sock; - - /* For non-blocking connect */ - struct sockadr *saddr; - size_t addrlen; - - /* Optional data and corresponding destructor users can use to provide - * context to a given redisContext. Not used by hiredis. */ - void *privdata; - void (*free_privdata)(void *); - - /* Internal context pointer presently used by hiredis to manage - * SSL connections. */ - void *privctx; - - /* An optional RESP3 PUSH handler */ - redisPushFn *push_cb; -} redisContext; - -redisContext *redisConnectWithOptions(const redisOptions *options); -redisContext *redisConnect(const char *ip, int port); -redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); -redisContext *redisConnectNonBlock(const char *ip, int port); -redisContext *redisConnectBindNonBlock(const char *ip, int port, - const char *source_addr); -redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, - const char *source_addr); -redisContext *redisConnectUnix(const char *path); -redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); -redisContext *redisConnectUnixNonBlock(const char *path); -redisContext *redisConnectFd(redisFD fd); - -/** - * Reconnect the given context using the saved information. - * - * This re-uses the exact same connect options as in the initial connection. - * host, ip (or path), timeout and bind address are reused, - * flags are used unmodified from the existing context. - * - * Returns REDIS_OK on successful connect or REDIS_ERR otherwise. - */ -int redisReconnect(redisContext *c); - -redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn); -int redisSetTimeout(redisContext *c, const struct timeval tv); -int redisEnableKeepAlive(redisContext *c); -void redisFree(redisContext *c); -redisFD redisFreeKeepFd(redisContext *c); -int redisBufferRead(redisContext *c); -int redisBufferWrite(redisContext *c, int *done); - -/* In a blocking context, this function first checks if there are unconsumed - * replies to return and returns one if so. Otherwise, it flushes the output - * buffer to the socket and reads until it has a reply. In a non-blocking - * context, it will return unconsumed replies until there are no more. */ -int redisGetReply(redisContext *c, void **reply); -int redisGetReplyFromReader(redisContext *c, void **reply); - -/* Write a formatted command to the output buffer. Use these functions in blocking mode - * to get a pipeline of commands. */ -int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len); - -/* Write a command to the output buffer. Use these functions in blocking mode - * to get a pipeline of commands. */ -int redisvAppendCommand(redisContext *c, const char *format, va_list ap); -int redisAppendCommand(redisContext *c, const char *format, ...); -int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); - -/* Issue a command to Redis. In a blocking context, it is identical to calling - * redisAppendCommand, followed by redisGetReply. The function will return - * NULL if there was an error in performing the request, otherwise it will - * return the reply. In a non-blocking context, it is identical to calling - * only redisAppendCommand and will always return NULL. */ -void *redisvCommand(redisContext *c, const char *format, va_list ap); -void *redisCommand(redisContext *c, const char *format, ...); -void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/ext/hiredis-1.0.2/include/hiredis/hiredis_ssl.h b/ext/hiredis-1.0.2/include/hiredis/hiredis_ssl.h deleted file mode 100644 index 604efe0c1..000000000 --- a/ext/hiredis-1.0.2/include/hiredis/hiredis_ssl.h +++ /dev/null @@ -1,127 +0,0 @@ - -/* - * Copyright (c) 2019, Redis Labs - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __HIREDIS_SSL_H -#define __HIREDIS_SSL_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* This is the underlying struct for SSL in ssl.h, which is not included to - * keep build dependencies short here. - */ -struct ssl_st; - -/* A wrapper around OpenSSL SSL_CTX to allow easy SSL use without directly - * calling OpenSSL. - */ -typedef struct redisSSLContext redisSSLContext; - -/** - * Initialization errors that redisCreateSSLContext() may return. - */ - -typedef enum { - REDIS_SSL_CTX_NONE = 0, /* No Error */ - REDIS_SSL_CTX_CREATE_FAILED, /* Failed to create OpenSSL SSL_CTX */ - REDIS_SSL_CTX_CERT_KEY_REQUIRED, /* Client cert and key must both be specified or skipped */ - REDIS_SSL_CTX_CA_CERT_LOAD_FAILED, /* Failed to load CA Certificate or CA Path */ - REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED, /* Failed to load client certificate */ - REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED /* Failed to load private key */ -} redisSSLContextError; - -/** - * Return the error message corresponding with the specified error code. - */ - -const char *redisSSLContextGetError(redisSSLContextError error); - -/** - * Helper function to initialize the OpenSSL library. - * - * OpenSSL requires one-time initialization before it can be used. Callers should - * call this function only once, and only if OpenSSL is not directly initialized - * elsewhere. - */ -int redisInitOpenSSL(void); - -/** - * Helper function to initialize an OpenSSL context that can be used - * to initiate SSL connections. - * - * cacert_filename is an optional name of a CA certificate/bundle file to load - * and use for validation. - * - * capath is an optional directory path where trusted CA certificate files are - * stored in an OpenSSL-compatible structure. - * - * cert_filename and private_key_filename are optional names of a client side - * certificate and private key files to use for authentication. They need to - * be both specified or omitted. - * - * server_name is an optional and will be used as a server name indication - * (SNI) TLS extension. - * - * If error is non-null, it will be populated in case the context creation fails - * (returning a NULL). - */ - -redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath, - const char *cert_filename, const char *private_key_filename, - const char *server_name, redisSSLContextError *error); - -/** - * Free a previously created OpenSSL context. - */ -void redisFreeSSLContext(redisSSLContext *redis_ssl_ctx); - -/** - * Initiate SSL on an existing redisContext. - * - * This is similar to redisInitiateSSL() but does not require the caller - * to directly interact with OpenSSL, and instead uses a redisSSLContext - * previously created using redisCreateSSLContext(). - */ - -int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx); - -/** - * Initiate SSL/TLS negotiation on a provided OpenSSL SSL object. - */ - -int redisInitiateSSL(redisContext *c, struct ssl_st *ssl); - -#ifdef __cplusplus -} -#endif - -#endif /* __HIREDIS_SSL_H */ diff --git a/ext/hiredis-1.0.2/include/hiredis/net.h b/ext/hiredis-1.0.2/include/hiredis/net.h deleted file mode 100644 index 9f43283a5..000000000 --- a/ext/hiredis-1.0.2/include/hiredis/net.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Extracted from anet.c to work properly with Hiredis error reporting. - * - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2014, Pieter Noordhuis - * Copyright (c) 2015, Matt Stancliff , - * Jan-Erik Rediger - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __NET_H -#define __NET_H - -#include "hiredis.h" - -void redisNetClose(redisContext *c); -ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap); -ssize_t redisNetWrite(redisContext *c); - -int redisCheckSocketError(redisContext *c); -int redisContextSetTimeout(redisContext *c, const struct timeval tv); -int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); -int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, - const struct timeval *timeout, - const char *source_addr); -int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout); -int redisKeepAlive(redisContext *c, int interval); -int redisCheckConnectDone(redisContext *c, int *completed); - -int redisSetTcpNoDelay(redisContext *c); - -#endif diff --git a/ext/hiredis-1.0.2/include/hiredis/read.h b/ext/hiredis-1.0.2/include/hiredis/read.h deleted file mode 100644 index 2d74d77a5..000000000 --- a/ext/hiredis-1.0.2/include/hiredis/read.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - - -#ifndef __HIREDIS_READ_H -#define __HIREDIS_READ_H -#include /* for size_t */ - -#define REDIS_ERR -1 -#define REDIS_OK 0 - -/* When an error occurs, the err flag in a context is set to hold the type of - * error that occurred. REDIS_ERR_IO means there was an I/O error and you - * should use the "errno" variable to find out what is wrong. - * For other values, the "errstr" field will hold a description. */ -#define REDIS_ERR_IO 1 /* Error in read or write */ -#define REDIS_ERR_EOF 3 /* End of file */ -#define REDIS_ERR_PROTOCOL 4 /* Protocol error */ -#define REDIS_ERR_OOM 5 /* Out of memory */ -#define REDIS_ERR_TIMEOUT 6 /* Timed out */ -#define REDIS_ERR_OTHER 2 /* Everything else... */ - -#define REDIS_REPLY_STRING 1 -#define REDIS_REPLY_ARRAY 2 -#define REDIS_REPLY_INTEGER 3 -#define REDIS_REPLY_NIL 4 -#define REDIS_REPLY_STATUS 5 -#define REDIS_REPLY_ERROR 6 -#define REDIS_REPLY_DOUBLE 7 -#define REDIS_REPLY_BOOL 8 -#define REDIS_REPLY_MAP 9 -#define REDIS_REPLY_SET 10 -#define REDIS_REPLY_ATTR 11 -#define REDIS_REPLY_PUSH 12 -#define REDIS_REPLY_BIGNUM 13 -#define REDIS_REPLY_VERB 14 - -/* Default max unused reader buffer. */ -#define REDIS_READER_MAX_BUF (1024*16) - -/* Default multi-bulk element limit */ -#define REDIS_READER_MAX_ARRAY_ELEMENTS ((1LL<<32) - 1) - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct redisReadTask { - int type; - long long elements; /* number of elements in multibulk container */ - int idx; /* index in parent (array) object */ - void *obj; /* holds user-generated value for a read task */ - struct redisReadTask *parent; /* parent task */ - void *privdata; /* user-settable arbitrary field */ -} redisReadTask; - -typedef struct redisReplyObjectFunctions { - void *(*createString)(const redisReadTask*, char*, size_t); - void *(*createArray)(const redisReadTask*, size_t); - void *(*createInteger)(const redisReadTask*, long long); - void *(*createDouble)(const redisReadTask*, double, char*, size_t); - void *(*createNil)(const redisReadTask*); - void *(*createBool)(const redisReadTask*, int); - void (*freeObject)(void*); -} redisReplyObjectFunctions; - -typedef struct redisReader { - int err; /* Error flags, 0 when there is no error */ - char errstr[128]; /* String representation of error when applicable */ - - char *buf; /* Read buffer */ - size_t pos; /* Buffer cursor */ - size_t len; /* Buffer length */ - size_t maxbuf; /* Max length of unused buffer */ - long long maxelements; /* Max multi-bulk elements */ - - redisReadTask **task; - int tasks; - - int ridx; /* Index of current read task */ - void *reply; /* Temporary reply pointer */ - - redisReplyObjectFunctions *fn; - void *privdata; -} redisReader; - -/* Public API for the protocol parser. */ -redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn); -void redisReaderFree(redisReader *r); -int redisReaderFeed(redisReader *r, const char *buf, size_t len); -int redisReaderGetReply(redisReader *r, void **reply); - -#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p)) -#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply) -#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/ext/hiredis-1.0.2/include/hiredis/sds.h b/ext/hiredis-1.0.2/include/hiredis/sds.h deleted file mode 100644 index eda8833b5..000000000 --- a/ext/hiredis-1.0.2/include/hiredis/sds.h +++ /dev/null @@ -1,278 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __SDS_H -#define __SDS_H - -#define SDS_MAX_PREALLOC (1024*1024) -#ifdef _MSC_VER -#define __attribute__(x) -typedef long long ssize_t; -#define SSIZE_MAX (LLONG_MAX >> 1) -#endif - -#include -#include -#include - -typedef char *sds; - -/* Note: sdshdr5 is never used, we just access the flags byte directly. - * However is here to document the layout of type 5 SDS strings. */ -struct __attribute__ ((__packed__)) sdshdr5 { - unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr8 { - uint8_t len; /* used */ - uint8_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr16 { - uint16_t len; /* used */ - uint16_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr32 { - uint32_t len; /* used */ - uint32_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr64 { - uint64_t len; /* used */ - uint64_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; - -#define SDS_TYPE_5 0 -#define SDS_TYPE_8 1 -#define SDS_TYPE_16 2 -#define SDS_TYPE_32 3 -#define SDS_TYPE_64 4 -#define SDS_TYPE_MASK 7 -#define SDS_TYPE_BITS 3 -#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))); -#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) -#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) - -static inline size_t sdslen(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return SDS_TYPE_5_LEN(flags); - case SDS_TYPE_8: - return SDS_HDR(8,s)->len; - case SDS_TYPE_16: - return SDS_HDR(16,s)->len; - case SDS_TYPE_32: - return SDS_HDR(32,s)->len; - case SDS_TYPE_64: - return SDS_HDR(64,s)->len; - } - return 0; -} - -static inline size_t sdsavail(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: { - return 0; - } - case SDS_TYPE_8: { - SDS_HDR_VAR(8,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_16: { - SDS_HDR_VAR(16,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_32: { - SDS_HDR_VAR(32,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_64: { - SDS_HDR_VAR(64,s); - return sh->alloc - sh->len; - } - } - return 0; -} - -static inline void sdssetlen(sds s, size_t newlen) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - { - unsigned char *fp = ((unsigned char*)s)-1; - *fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS)); - } - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->len = (uint8_t)newlen; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->len = (uint16_t)newlen; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->len = (uint32_t)newlen; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->len = (uint64_t)newlen; - break; - } -} - -static inline void sdsinclen(sds s, size_t inc) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - { - unsigned char *fp = ((unsigned char*)s)-1; - unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc; - *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); - } - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->len += (uint8_t)inc; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->len += (uint16_t)inc; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->len += (uint32_t)inc; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->len += (uint64_t)inc; - break; - } -} - -/* sdsalloc() = sdsavail() + sdslen() */ -static inline size_t sdsalloc(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return SDS_TYPE_5_LEN(flags); - case SDS_TYPE_8: - return SDS_HDR(8,s)->alloc; - case SDS_TYPE_16: - return SDS_HDR(16,s)->alloc; - case SDS_TYPE_32: - return SDS_HDR(32,s)->alloc; - case SDS_TYPE_64: - return SDS_HDR(64,s)->alloc; - } - return 0; -} - -static inline void sdssetalloc(sds s, size_t newlen) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - /* Nothing to do, this type has no total allocation info. */ - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->alloc = (uint8_t)newlen; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->alloc = (uint16_t)newlen; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->alloc = (uint32_t)newlen; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->alloc = (uint64_t)newlen; - break; - } -} - -sds sdsnewlen(const void *init, size_t initlen); -sds sdsnew(const char *init); -sds sdsempty(void); -sds sdsdup(const sds s); -void sdsfree(sds s); -sds sdsgrowzero(sds s, size_t len); -sds sdscatlen(sds s, const void *t, size_t len); -sds sdscat(sds s, const char *t); -sds sdscatsds(sds s, const sds t); -sds sdscpylen(sds s, const char *t, size_t len); -sds sdscpy(sds s, const char *t); - -sds sdscatvprintf(sds s, const char *fmt, va_list ap); -#ifdef __GNUC__ -sds sdscatprintf(sds s, const char *fmt, ...) - __attribute__((format(printf, 2, 3))); -#else -sds sdscatprintf(sds s, const char *fmt, ...); -#endif - -sds sdscatfmt(sds s, char const *fmt, ...); -sds sdstrim(sds s, const char *cset); -int sdsrange(sds s, ssize_t start, ssize_t end); -void sdsupdatelen(sds s); -void sdsclear(sds s); -int sdscmp(const sds s1, const sds s2); -sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); -void sdsfreesplitres(sds *tokens, int count); -void sdstolower(sds s); -void sdstoupper(sds s); -sds sdsfromlonglong(long long value); -sds sdscatrepr(sds s, const char *p, size_t len); -sds *sdssplitargs(const char *line, int *argc); -sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); -sds sdsjoin(char **argv, int argc, char *sep); -sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); - -/* Low level functions exposed to the user API */ -sds sdsMakeRoomFor(sds s, size_t addlen); -void sdsIncrLen(sds s, int incr); -sds sdsRemoveFreeSpace(sds s); -size_t sdsAllocSize(sds s); -void *sdsAllocPtr(sds s); - -/* Export the allocator used by SDS to the program using SDS. - * Sometimes the program SDS is linked to, may use a different set of - * allocators, but may want to allocate or free things that SDS will - * respectively free or allocate. */ -void *sds_malloc(size_t size); -void *sds_realloc(void *ptr, size_t size); -void sds_free(void *ptr); - -#ifdef REDIS_TEST -int sdsTest(int argc, char *argv[]); -#endif - -#endif diff --git a/ext/hiredis-1.0.2/include/hiredis/sdsalloc.h b/ext/hiredis-1.0.2/include/hiredis/sdsalloc.h deleted file mode 100644 index 5538dd94c..000000000 --- a/ext/hiredis-1.0.2/include/hiredis/sdsalloc.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/* SDS allocator selection. - * - * This file is used in order to change the SDS allocator at compile time. - * Just define the following defines to what you want to use. Also add - * the include of your alternate allocator if needed (not needed in order - * to use the default libc allocator). */ - -#include "alloc.h" - -#define s_malloc hi_malloc -#define s_realloc hi_realloc -#define s_free hi_free diff --git a/ext/hiredis-1.0.2/include/hiredis/sockcompat.h b/ext/hiredis-1.0.2/include/hiredis/sockcompat.h deleted file mode 100644 index 85810e848..000000000 --- a/ext/hiredis-1.0.2/include/hiredis/sockcompat.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2019, Marcus Geelnard - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __SOCKCOMPAT_H -#define __SOCKCOMPAT_H - -#ifndef _WIN32 -/* For POSIX systems we use the standard BSD socket API. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#else -/* For Windows we use winsock. */ -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 /* To get WSAPoll etc. */ -#include -#include -#include -#include - -#ifdef _MSC_VER -typedef long long ssize_t; -#endif - -/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */ -int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); -const char *win32_gai_strerror(int errcode); -void win32_freeaddrinfo(struct addrinfo *res); -SOCKET win32_socket(int domain, int type, int protocol); -int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp); -int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen); -int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen); -int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen); -int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen); -int win32_close(SOCKET fd); -ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags); -ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags); -typedef ULONG nfds_t; -int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout); - -#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION -#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res) -#undef gai_strerror -#define gai_strerror(errcode) win32_gai_strerror(errcode) -#define freeaddrinfo(res) win32_freeaddrinfo(res) -#define socket(domain, type, protocol) win32_socket(domain, type, protocol) -#define ioctl(fd, request, argp) win32_ioctl(fd, request, argp) -#define bind(sockfd, addr, addrlen) win32_bind(sockfd, addr, addrlen) -#define connect(sockfd, addr, addrlen) win32_connect(sockfd, addr, addrlen) -#define getsockopt(sockfd, level, optname, optval, optlen) win32_getsockopt(sockfd, level, optname, optval, optlen) -#define setsockopt(sockfd, level, optname, optval, optlen) win32_setsockopt(sockfd, level, optname, optval, optlen) -#define close(fd) win32_close(fd) -#define recv(sockfd, buf, len, flags) win32_recv(sockfd, buf, len, flags) -#define send(sockfd, buf, len, flags) win32_send(sockfd, buf, len, flags) -#define poll(fds, nfds, timeout) win32_poll(fds, nfds, timeout) -#endif /* REDIS_SOCKCOMPAT_IMPLEMENTATION */ -#endif /* _WIN32 */ - -#endif /* __SOCKCOMPAT_H */ diff --git a/ext/hiredis-1.0.2/include/hiredis/win32.h b/ext/hiredis-1.0.2/include/hiredis/win32.h deleted file mode 100644 index 04289c696..000000000 --- a/ext/hiredis-1.0.2/include/hiredis/win32.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef _WIN32_HELPER_INCLUDE -#define _WIN32_HELPER_INCLUDE -#ifdef _MSC_VER - -#include /* for struct timeval */ - -#ifndef inline -#define inline __inline -#endif - -#ifndef strcasecmp -#define strcasecmp stricmp -#endif - -#ifndef strncasecmp -#define strncasecmp strnicmp -#endif - -#ifndef va_copy -#define va_copy(d,s) ((d) = (s)) -#endif - -#ifndef snprintf -#define snprintf c99_snprintf - -__inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) -{ - int count = -1; - - if (size != 0) - count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); - if (count == -1) - count = _vscprintf(format, ap); - - return count; -} - -__inline int c99_snprintf(char* str, size_t size, const char* format, ...) -{ - int count; - va_list ap; - - va_start(ap, format); - count = c99_vsnprintf(str, size, format, ap); - va_end(ap); - - return count; -} -#endif -#endif /* _MSC_VER */ - -#ifdef _WIN32 -#define strerror_r(errno,buf,len) strerror_s(buf,len,errno) -#endif /* _WIN32 */ - -#endif /* _WIN32_HELPER_INCLUDE */ diff --git a/ext/hiredis-1.0.2/lib/ubuntu22.04/amd64/libhiredis.a b/ext/hiredis-1.0.2/lib/ubuntu22.04/amd64/libhiredis.a deleted file mode 100644 index af1314f01..000000000 Binary files a/ext/hiredis-1.0.2/lib/ubuntu22.04/amd64/libhiredis.a and /dev/null differ diff --git a/ext/hiredis-1.0.2/lib/ubuntu22.04/arm64/libhiredis.a b/ext/hiredis-1.0.2/lib/ubuntu22.04/arm64/libhiredis.a deleted file mode 100644 index 081c92705..000000000 Binary files a/ext/hiredis-1.0.2/lib/ubuntu22.04/arm64/libhiredis.a and /dev/null differ diff --git a/ext/hiredis-1.0.2/net.c b/ext/hiredis-1.0.2/net.c deleted file mode 100644 index c6b0e5d8e..000000000 --- a/ext/hiredis-1.0.2/net.c +++ /dev/null @@ -1,612 +0,0 @@ -/* Extracted from anet.c to work properly with Hiredis error reporting. - * - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2014, Pieter Noordhuis - * Copyright (c) 2015, Matt Stancliff , - * Jan-Erik Rediger - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "fmacros.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "net.h" -#include "sds.h" -#include "sockcompat.h" -#include "win32.h" - -/* Defined in hiredis.c */ -void __redisSetError(redisContext *c, int type, const char *str); - -void redisNetClose(redisContext *c) { - if (c && c->fd != REDIS_INVALID_FD) { - close(c->fd); - c->fd = REDIS_INVALID_FD; - } -} - -ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) { - ssize_t nread = recv(c->fd, buf, bufcap, 0); - if (nread == -1) { - if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { - /* Try again later */ - return 0; - } else if(errno == ETIMEDOUT && (c->flags & REDIS_BLOCK)) { - /* especially in windows */ - __redisSetError(c, REDIS_ERR_TIMEOUT, "recv timeout"); - return -1; - } else { - __redisSetError(c, REDIS_ERR_IO, NULL); - return -1; - } - } else if (nread == 0) { - __redisSetError(c, REDIS_ERR_EOF, "Server closed the connection"); - return -1; - } else { - return nread; - } -} - -ssize_t redisNetWrite(redisContext *c) { - ssize_t nwritten = send(c->fd, c->obuf, sdslen(c->obuf), 0); - if (nwritten < 0) { - if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { - /* Try again later */ - } else { - __redisSetError(c, REDIS_ERR_IO, NULL); - return -1; - } - } - return nwritten; -} - -static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) { - int errorno = errno; /* snprintf() may change errno */ - char buf[128] = { 0 }; - size_t len = 0; - - if (prefix != NULL) - len = snprintf(buf,sizeof(buf),"%s: ",prefix); - strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len); - __redisSetError(c,type,buf); -} - -static int redisSetReuseAddr(redisContext *c) { - int on = 1; - if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - redisNetClose(c); - return REDIS_ERR; - } - return REDIS_OK; -} - -static int redisCreateSocket(redisContext *c, int type) { - redisFD s; - if ((s = socket(type, SOCK_STREAM, 0)) == REDIS_INVALID_FD) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - return REDIS_ERR; - } - c->fd = s; - if (type == AF_INET) { - if (redisSetReuseAddr(c) == REDIS_ERR) { - return REDIS_ERR; - } - } - return REDIS_OK; -} - -static int redisSetBlocking(redisContext *c, int blocking) { -#ifndef _WIN32 - int flags; - - /* Set the socket nonblocking. - * Note that fcntl(2) for F_GETFL and F_SETFL can't be - * interrupted by a signal. */ - if ((flags = fcntl(c->fd, F_GETFL)) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)"); - redisNetClose(c); - return REDIS_ERR; - } - - if (blocking) - flags &= ~O_NONBLOCK; - else - flags |= O_NONBLOCK; - - if (fcntl(c->fd, F_SETFL, flags) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)"); - redisNetClose(c); - return REDIS_ERR; - } -#else - u_long mode = blocking ? 0 : 1; - if (ioctl(c->fd, FIONBIO, &mode) == -1) { - __redisSetErrorFromErrno(c, REDIS_ERR_IO, "ioctl(FIONBIO)"); - redisNetClose(c); - return REDIS_ERR; - } -#endif /* _WIN32 */ - return REDIS_OK; -} - -int redisKeepAlive(redisContext *c, int interval) { - int val = 1; - redisFD fd = c->fd; - - if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){ - __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); - return REDIS_ERR; - } - - val = interval; - -#if defined(__APPLE__) && defined(__MACH__) - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) { - __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); - return REDIS_ERR; - } -#else -#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__) - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) { - __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); - return REDIS_ERR; - } - - val = interval/3; - if (val == 0) val = 1; - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) { - __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); - return REDIS_ERR; - } - - val = 3; - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) { - __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); - return REDIS_ERR; - } -#endif -#endif - - return REDIS_OK; -} - -int redisSetTcpNoDelay(redisContext *c) { - int yes = 1; - if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)"); - redisNetClose(c); - return REDIS_ERR; - } - return REDIS_OK; -} - -#define __MAX_MSEC (((LONG_MAX) - 999) / 1000) - -static int redisContextTimeoutMsec(redisContext *c, long *result) -{ - const struct timeval *timeout = c->connect_timeout; - long msec = -1; - - /* Only use timeout when not NULL. */ - if (timeout != NULL) { - if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) { - *result = msec; - return REDIS_ERR; - } - - msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000); - - if (msec < 0 || msec > INT_MAX) { - msec = INT_MAX; - } - } - - *result = msec; - return REDIS_OK; -} - -static int redisContextWaitReady(redisContext *c, long msec) { - struct pollfd wfd[1]; - - wfd[0].fd = c->fd; - wfd[0].events = POLLOUT; - - if (errno == EINPROGRESS) { - int res; - - if ((res = poll(wfd, 1, msec)) == -1) { - __redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)"); - redisNetClose(c); - return REDIS_ERR; - } else if (res == 0) { - errno = ETIMEDOUT; - __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - redisNetClose(c); - return REDIS_ERR; - } - - if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) { - redisCheckSocketError(c); - return REDIS_ERR; - } - - return REDIS_OK; - } - - __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - redisNetClose(c); - return REDIS_ERR; -} - -int redisCheckConnectDone(redisContext *c, int *completed) { - int rc = connect(c->fd, (const struct sockaddr *)c->saddr, c->addrlen); - if (rc == 0) { - *completed = 1; - return REDIS_OK; - } - switch (errno) { - case EISCONN: - *completed = 1; - return REDIS_OK; - case EALREADY: - case EINPROGRESS: - case EWOULDBLOCK: - *completed = 0; - return REDIS_OK; - default: - return REDIS_ERR; - } -} - -int redisCheckSocketError(redisContext *c) { - int err = 0, errno_saved = errno; - socklen_t errlen = sizeof(err); - - if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)"); - return REDIS_ERR; - } - - if (err == 0) { - err = errno_saved; - } - - if (err) { - errno = err; - __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - return REDIS_ERR; - } - - return REDIS_OK; -} - -int redisContextSetTimeout(redisContext *c, const struct timeval tv) { - const void *to_ptr = &tv; - size_t to_sz = sizeof(tv); - - if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)"); - return REDIS_ERR; - } - if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) { - __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)"); - return REDIS_ERR; - } - return REDIS_OK; -} - -int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout) { - /* Same timeval struct, short circuit */ - if (c->connect_timeout == timeout) - return REDIS_OK; - - /* Allocate context timeval if we need to */ - if (c->connect_timeout == NULL) { - c->connect_timeout = hi_malloc(sizeof(*c->connect_timeout)); - if (c->connect_timeout == NULL) - return REDIS_ERR; - } - - memcpy(c->connect_timeout, timeout, sizeof(*c->connect_timeout)); - return REDIS_OK; -} - -int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout) { - /* Same timeval struct, short circuit */ - if (c->command_timeout == timeout) - return REDIS_OK; - - /* Allocate context timeval if we need to */ - if (c->command_timeout == NULL) { - c->command_timeout = hi_malloc(sizeof(*c->command_timeout)); - if (c->command_timeout == NULL) - return REDIS_ERR; - } - - memcpy(c->command_timeout, timeout, sizeof(*c->command_timeout)); - return REDIS_OK; -} - -static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, - const struct timeval *timeout, - const char *source_addr) { - redisFD s; - int rv, n; - char _port[6]; /* strlen("65535"); */ - struct addrinfo hints, *servinfo, *bservinfo, *p, *b; - int blocking = (c->flags & REDIS_BLOCK); - int reuseaddr = (c->flags & REDIS_REUSEADDR); - int reuses = 0; - long timeout_msec = -1; - - servinfo = NULL; - c->connection_type = REDIS_CONN_TCP; - c->tcp.port = port; - - /* We need to take possession of the passed parameters - * to make them reusable for a reconnect. - * We also carefully check we don't free data we already own, - * as in the case of the reconnect method. - * - * This is a bit ugly, but atleast it works and doesn't leak memory. - **/ - if (c->tcp.host != addr) { - hi_free(c->tcp.host); - - c->tcp.host = hi_strdup(addr); - if (c->tcp.host == NULL) - goto oom; - } - - if (timeout) { - if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR) - goto oom; - } else { - hi_free(c->connect_timeout); - c->connect_timeout = NULL; - } - - if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) { - __redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified"); - goto error; - } - - if (source_addr == NULL) { - hi_free(c->tcp.source_addr); - c->tcp.source_addr = NULL; - } else if (c->tcp.source_addr != source_addr) { - hi_free(c->tcp.source_addr); - c->tcp.source_addr = hi_strdup(source_addr); - } - - snprintf(_port, 6, "%d", port); - memset(&hints,0,sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - - /* Try with IPv6 if no IPv4 address was found. We do it in this order since - * in a Redis client you can't afford to test if you have IPv6 connectivity - * as this would add latency to every connect. Otherwise a more sensible - * route could be: Use IPv6 if both addresses are available and there is IPv6 - * connectivity. */ - if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) { - hints.ai_family = AF_INET6; - if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) { - __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv)); - return REDIS_ERR; - } - } - for (p = servinfo; p != NULL; p = p->ai_next) { -addrretry: - if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD) - continue; - - c->fd = s; - if (redisSetBlocking(c,0) != REDIS_OK) - goto error; - if (c->tcp.source_addr) { - int bound = 0; - /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */ - if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) { - char buf[128]; - snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv)); - __redisSetError(c,REDIS_ERR_OTHER,buf); - goto error; - } - - if (reuseaddr) { - n = 1; - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n, - sizeof(n)) < 0) { - freeaddrinfo(bservinfo); - goto error; - } - } - - for (b = bservinfo; b != NULL; b = b->ai_next) { - if (bind(s,b->ai_addr,b->ai_addrlen) != -1) { - bound = 1; - break; - } - } - freeaddrinfo(bservinfo); - if (!bound) { - char buf[128]; - snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno)); - __redisSetError(c,REDIS_ERR_OTHER,buf); - goto error; - } - } - - /* For repeat connection */ - hi_free(c->saddr); - c->saddr = hi_malloc(p->ai_addrlen); - if (c->saddr == NULL) - goto oom; - - memcpy(c->saddr, p->ai_addr, p->ai_addrlen); - c->addrlen = p->ai_addrlen; - - if (connect(s,p->ai_addr,p->ai_addrlen) == -1) { - if (errno == EHOSTUNREACH) { - redisNetClose(c); - continue; - } else if (errno == EINPROGRESS) { - if (blocking) { - goto wait_for_ready; - } - /* This is ok. - * Note that even when it's in blocking mode, we unset blocking - * for `connect()` - */ - } else if (errno == EADDRNOTAVAIL && reuseaddr) { - if (++reuses >= REDIS_CONNECT_RETRIES) { - goto error; - } else { - redisNetClose(c); - goto addrretry; - } - } else { - wait_for_ready: - if (redisContextWaitReady(c,timeout_msec) != REDIS_OK) - goto error; - if (redisSetTcpNoDelay(c) != REDIS_OK) - goto error; - } - } - if (blocking && redisSetBlocking(c,1) != REDIS_OK) - goto error; - - c->flags |= REDIS_CONNECTED; - rv = REDIS_OK; - goto end; - } - if (p == NULL) { - char buf[128]; - snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno)); - __redisSetError(c,REDIS_ERR_OTHER,buf); - goto error; - } - -oom: - __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); -error: - rv = REDIS_ERR; -end: - if(servinfo) { - freeaddrinfo(servinfo); - } - - return rv; // Need to return REDIS_OK if alright -} - -int redisContextConnectTcp(redisContext *c, const char *addr, int port, - const struct timeval *timeout) { - return _redisContextConnectTcp(c, addr, port, timeout, NULL); -} - -int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, - const struct timeval *timeout, - const char *source_addr) { - return _redisContextConnectTcp(c, addr, port, timeout, source_addr); -} - -int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) { -#ifndef _WIN32 - int blocking = (c->flags & REDIS_BLOCK); - struct sockaddr_un *sa; - long timeout_msec = -1; - - if (redisCreateSocket(c,AF_UNIX) < 0) - return REDIS_ERR; - if (redisSetBlocking(c,0) != REDIS_OK) - return REDIS_ERR; - - c->connection_type = REDIS_CONN_UNIX; - if (c->unix_sock.path != path) { - hi_free(c->unix_sock.path); - - c->unix_sock.path = hi_strdup(path); - if (c->unix_sock.path == NULL) - goto oom; - } - - if (timeout) { - if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR) - goto oom; - } else { - hi_free(c->connect_timeout); - c->connect_timeout = NULL; - } - - if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK) - return REDIS_ERR; - - /* Don't leak sockaddr if we're reconnecting */ - if (c->saddr) hi_free(c->saddr); - - sa = (struct sockaddr_un*)(c->saddr = hi_malloc(sizeof(struct sockaddr_un))); - if (sa == NULL) - goto oom; - - c->addrlen = sizeof(struct sockaddr_un); - sa->sun_family = AF_UNIX; - strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1); - if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) { - if (errno == EINPROGRESS && !blocking) { - /* This is ok. */ - } else { - if (redisContextWaitReady(c,timeout_msec) != REDIS_OK) - return REDIS_ERR; - } - } - - /* Reset socket to be blocking after connect(2). */ - if (blocking && redisSetBlocking(c,1) != REDIS_OK) - return REDIS_ERR; - - c->flags |= REDIS_CONNECTED; - return REDIS_OK; -#else - /* We currently do not support Unix sockets for Windows. */ - /* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */ - errno = EPROTONOSUPPORT; - return REDIS_ERR; -#endif /* _WIN32 */ -oom: - __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); - return REDIS_ERR; -} diff --git a/ext/hiredis-1.0.2/net.h b/ext/hiredis-1.0.2/net.h deleted file mode 100644 index 9f43283a5..000000000 --- a/ext/hiredis-1.0.2/net.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Extracted from anet.c to work properly with Hiredis error reporting. - * - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2014, Pieter Noordhuis - * Copyright (c) 2015, Matt Stancliff , - * Jan-Erik Rediger - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __NET_H -#define __NET_H - -#include "hiredis.h" - -void redisNetClose(redisContext *c); -ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap); -ssize_t redisNetWrite(redisContext *c); - -int redisCheckSocketError(redisContext *c); -int redisContextSetTimeout(redisContext *c, const struct timeval tv); -int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); -int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, - const struct timeval *timeout, - const char *source_addr); -int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout); -int redisKeepAlive(redisContext *c, int interval); -int redisCheckConnectDone(redisContext *c, int *completed); - -int redisSetTcpNoDelay(redisContext *c); - -#endif diff --git a/ext/hiredis-1.0.2/read.c b/ext/hiredis-1.0.2/read.c deleted file mode 100644 index 09524692b..000000000 --- a/ext/hiredis-1.0.2/read.c +++ /dev/null @@ -1,739 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "fmacros.h" -#include -#include -#ifndef _MSC_VER -#include -#include -#endif -#include -#include -#include -#include -#include - -#include "alloc.h" -#include "read.h" -#include "sds.h" -#include "win32.h" - -/* Initial size of our nested reply stack and how much we grow it when needd */ -#define REDIS_READER_STACK_SIZE 9 - -static void __redisReaderSetError(redisReader *r, int type, const char *str) { - size_t len; - - if (r->reply != NULL && r->fn && r->fn->freeObject) { - r->fn->freeObject(r->reply); - r->reply = NULL; - } - - /* Clear input buffer on errors. */ - sdsfree(r->buf); - r->buf = NULL; - r->pos = r->len = 0; - - /* Reset task stack. */ - r->ridx = -1; - - /* Set error. */ - r->err = type; - len = strlen(str); - len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1); - memcpy(r->errstr,str,len); - r->errstr[len] = '\0'; -} - -static size_t chrtos(char *buf, size_t size, char byte) { - size_t len = 0; - - switch(byte) { - case '\\': - case '"': - len = snprintf(buf,size,"\"\\%c\"",byte); - break; - case '\n': len = snprintf(buf,size,"\"\\n\""); break; - case '\r': len = snprintf(buf,size,"\"\\r\""); break; - case '\t': len = snprintf(buf,size,"\"\\t\""); break; - case '\a': len = snprintf(buf,size,"\"\\a\""); break; - case '\b': len = snprintf(buf,size,"\"\\b\""); break; - default: - if (isprint(byte)) - len = snprintf(buf,size,"\"%c\"",byte); - else - len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte); - break; - } - - return len; -} - -static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) { - char cbuf[8], sbuf[128]; - - chrtos(cbuf,sizeof(cbuf),byte); - snprintf(sbuf,sizeof(sbuf), - "Protocol error, got %s as reply type byte", cbuf); - __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf); -} - -static void __redisReaderSetErrorOOM(redisReader *r) { - __redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory"); -} - -static char *readBytes(redisReader *r, unsigned int bytes) { - char *p; - if (r->len-r->pos >= bytes) { - p = r->buf+r->pos; - r->pos += bytes; - return p; - } - return NULL; -} - -/* Find pointer to \r\n. */ -static char *seekNewline(char *s, size_t len) { - int pos = 0; - int _len = len-1; - - /* Position should be < len-1 because the character at "pos" should be - * followed by a \n. Note that strchr cannot be used because it doesn't - * allow to search a limited length and the buffer that is being searched - * might not have a trailing NULL character. */ - while (pos < _len) { - while(pos < _len && s[pos] != '\r') pos++; - if (pos==_len) { - /* Not found. */ - return NULL; - } else { - if (s[pos+1] == '\n') { - /* Found. */ - return s+pos; - } else { - /* Continue searching. */ - pos++; - } - } - } - return NULL; -} - -/* Convert a string into a long long. Returns REDIS_OK if the string could be - * parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value - * will be set to the parsed value when appropriate. - * - * Note that this function demands that the string strictly represents - * a long long: no spaces or other characters before or after the string - * representing the number are accepted, nor zeroes at the start if not - * for the string "0" representing the zero number. - * - * Because of its strictness, it is safe to use this function to check if - * you can convert a string into a long long, and obtain back the string - * from the number without any loss in the string representation. */ -static int string2ll(const char *s, size_t slen, long long *value) { - const char *p = s; - size_t plen = 0; - int negative = 0; - unsigned long long v; - - if (plen == slen) - return REDIS_ERR; - - /* Special case: first and only digit is 0. */ - if (slen == 1 && p[0] == '0') { - if (value != NULL) *value = 0; - return REDIS_OK; - } - - if (p[0] == '-') { - negative = 1; - p++; plen++; - - /* Abort on only a negative sign. */ - if (plen == slen) - return REDIS_ERR; - } - - /* First digit should be 1-9, otherwise the string should just be 0. */ - if (p[0] >= '1' && p[0] <= '9') { - v = p[0]-'0'; - p++; plen++; - } else if (p[0] == '0' && slen == 1) { - *value = 0; - return REDIS_OK; - } else { - return REDIS_ERR; - } - - while (plen < slen && p[0] >= '0' && p[0] <= '9') { - if (v > (ULLONG_MAX / 10)) /* Overflow. */ - return REDIS_ERR; - v *= 10; - - if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */ - return REDIS_ERR; - v += p[0]-'0'; - - p++; plen++; - } - - /* Return if not all bytes were used. */ - if (plen < slen) - return REDIS_ERR; - - if (negative) { - if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */ - return REDIS_ERR; - if (value != NULL) *value = -v; - } else { - if (v > LLONG_MAX) /* Overflow. */ - return REDIS_ERR; - if (value != NULL) *value = v; - } - return REDIS_OK; -} - -static char *readLine(redisReader *r, int *_len) { - char *p, *s; - int len; - - p = r->buf+r->pos; - s = seekNewline(p,(r->len-r->pos)); - if (s != NULL) { - len = s-(r->buf+r->pos); - r->pos += len+2; /* skip \r\n */ - if (_len) *_len = len; - return p; - } - return NULL; -} - -static void moveToNextTask(redisReader *r) { - redisReadTask *cur, *prv; - while (r->ridx >= 0) { - /* Return a.s.a.p. when the stack is now empty. */ - if (r->ridx == 0) { - r->ridx--; - return; - } - - cur = r->task[r->ridx]; - prv = r->task[r->ridx-1]; - assert(prv->type == REDIS_REPLY_ARRAY || - prv->type == REDIS_REPLY_MAP || - prv->type == REDIS_REPLY_SET || - prv->type == REDIS_REPLY_PUSH); - if (cur->idx == prv->elements-1) { - r->ridx--; - } else { - /* Reset the type because the next item can be anything */ - assert(cur->idx < prv->elements); - cur->type = -1; - cur->elements = -1; - cur->idx++; - return; - } - } -} - -static int processLineItem(redisReader *r) { - redisReadTask *cur = r->task[r->ridx]; - void *obj; - char *p; - int len; - - if ((p = readLine(r,&len)) != NULL) { - if (cur->type == REDIS_REPLY_INTEGER) { - if (r->fn && r->fn->createInteger) { - long long v; - if (string2ll(p, len, &v) == REDIS_ERR) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Bad integer value"); - return REDIS_ERR; - } - obj = r->fn->createInteger(cur,v); - } else { - obj = (void*)REDIS_REPLY_INTEGER; - } - } else if (cur->type == REDIS_REPLY_DOUBLE) { - if (r->fn && r->fn->createDouble) { - char buf[326], *eptr; - double d; - - if ((size_t)len >= sizeof(buf)) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Double value is too large"); - return REDIS_ERR; - } - - memcpy(buf,p,len); - buf[len] = '\0'; - - if (strcasecmp(buf,",inf") == 0) { - d = INFINITY; /* Positive infinite. */ - } else if (strcasecmp(buf,",-inf") == 0) { - d = -INFINITY; /* Negative infinite. */ - } else { - d = strtod((char*)buf,&eptr); - if (buf[0] == '\0' || eptr[0] != '\0' || isnan(d)) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Bad double value"); - return REDIS_ERR; - } - } - obj = r->fn->createDouble(cur,d,buf,len); - } else { - obj = (void*)REDIS_REPLY_DOUBLE; - } - } else if (cur->type == REDIS_REPLY_NIL) { - if (r->fn && r->fn->createNil) - obj = r->fn->createNil(cur); - else - obj = (void*)REDIS_REPLY_NIL; - } else if (cur->type == REDIS_REPLY_BOOL) { - int bval = p[0] == 't' || p[0] == 'T'; - if (r->fn && r->fn->createBool) - obj = r->fn->createBool(cur,bval); - else - obj = (void*)REDIS_REPLY_BOOL; - } else { - /* Type will be error or status. */ - if (r->fn && r->fn->createString) - obj = r->fn->createString(cur,p,len); - else - obj = (void*)(size_t)(cur->type); - } - - if (obj == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } - - /* Set reply if this is the root object. */ - if (r->ridx == 0) r->reply = obj; - moveToNextTask(r); - return REDIS_OK; - } - - return REDIS_ERR; -} - -static int processBulkItem(redisReader *r) { - redisReadTask *cur = r->task[r->ridx]; - void *obj = NULL; - char *p, *s; - long long len; - unsigned long bytelen; - int success = 0; - - p = r->buf+r->pos; - s = seekNewline(p,r->len-r->pos); - if (s != NULL) { - p = r->buf+r->pos; - bytelen = s-(r->buf+r->pos)+2; /* include \r\n */ - - if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Bad bulk string length"); - return REDIS_ERR; - } - - if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Bulk string length out of range"); - return REDIS_ERR; - } - - if (len == -1) { - /* The nil object can always be created. */ - if (r->fn && r->fn->createNil) - obj = r->fn->createNil(cur); - else - obj = (void*)REDIS_REPLY_NIL; - success = 1; - } else { - /* Only continue when the buffer contains the entire bulk item. */ - bytelen += len+2; /* include \r\n */ - if (r->pos+bytelen <= r->len) { - if ((cur->type == REDIS_REPLY_VERB && len < 4) || - (cur->type == REDIS_REPLY_VERB && s[5] != ':')) - { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Verbatim string 4 bytes of content type are " - "missing or incorrectly encoded."); - return REDIS_ERR; - } - if (r->fn && r->fn->createString) - obj = r->fn->createString(cur,s+2,len); - else - obj = (void*)(long)cur->type; - success = 1; - } - } - - /* Proceed when obj was created. */ - if (success) { - if (obj == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } - - r->pos += bytelen; - - /* Set reply if this is the root object. */ - if (r->ridx == 0) r->reply = obj; - moveToNextTask(r); - return REDIS_OK; - } - } - - return REDIS_ERR; -} - -static int redisReaderGrow(redisReader *r) { - redisReadTask **aux; - int newlen; - - /* Grow our stack size */ - newlen = r->tasks + REDIS_READER_STACK_SIZE; - aux = hi_realloc(r->task, sizeof(*r->task) * newlen); - if (aux == NULL) - goto oom; - - r->task = aux; - - /* Allocate new tasks */ - for (; r->tasks < newlen; r->tasks++) { - r->task[r->tasks] = hi_calloc(1, sizeof(**r->task)); - if (r->task[r->tasks] == NULL) - goto oom; - } - - return REDIS_OK; -oom: - __redisReaderSetErrorOOM(r); - return REDIS_ERR; -} - -/* Process the array, map and set types. */ -static int processAggregateItem(redisReader *r) { - redisReadTask *cur = r->task[r->ridx]; - void *obj; - char *p; - long long elements; - int root = 0, len; - - /* Set error for nested multi bulks with depth > 7 */ - if (r->ridx == r->tasks - 1) { - if (redisReaderGrow(r) == REDIS_ERR) - return REDIS_ERR; - } - - if ((p = readLine(r,&len)) != NULL) { - if (string2ll(p, len, &elements) == REDIS_ERR) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Bad multi-bulk length"); - return REDIS_ERR; - } - - root = (r->ridx == 0); - - if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX) || - (r->maxelements > 0 && elements > r->maxelements)) - { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Multi-bulk length out of range"); - return REDIS_ERR; - } - - if (elements == -1) { - if (r->fn && r->fn->createNil) - obj = r->fn->createNil(cur); - else - obj = (void*)REDIS_REPLY_NIL; - - if (obj == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } - - moveToNextTask(r); - } else { - if (cur->type == REDIS_REPLY_MAP) elements *= 2; - - if (r->fn && r->fn->createArray) - obj = r->fn->createArray(cur,elements); - else - obj = (void*)(long)cur->type; - - if (obj == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } - - /* Modify task stack when there are more than 0 elements. */ - if (elements > 0) { - cur->elements = elements; - cur->obj = obj; - r->ridx++; - r->task[r->ridx]->type = -1; - r->task[r->ridx]->elements = -1; - r->task[r->ridx]->idx = 0; - r->task[r->ridx]->obj = NULL; - r->task[r->ridx]->parent = cur; - r->task[r->ridx]->privdata = r->privdata; - } else { - moveToNextTask(r); - } - } - - /* Set reply if this is the root object. */ - if (root) r->reply = obj; - return REDIS_OK; - } - - return REDIS_ERR; -} - -static int processItem(redisReader *r) { - redisReadTask *cur = r->task[r->ridx]; - char *p; - - /* check if we need to read type */ - if (cur->type < 0) { - if ((p = readBytes(r,1)) != NULL) { - switch (p[0]) { - case '-': - cur->type = REDIS_REPLY_ERROR; - break; - case '+': - cur->type = REDIS_REPLY_STATUS; - break; - case ':': - cur->type = REDIS_REPLY_INTEGER; - break; - case ',': - cur->type = REDIS_REPLY_DOUBLE; - break; - case '_': - cur->type = REDIS_REPLY_NIL; - break; - case '$': - cur->type = REDIS_REPLY_STRING; - break; - case '*': - cur->type = REDIS_REPLY_ARRAY; - break; - case '%': - cur->type = REDIS_REPLY_MAP; - break; - case '~': - cur->type = REDIS_REPLY_SET; - break; - case '#': - cur->type = REDIS_REPLY_BOOL; - break; - case '=': - cur->type = REDIS_REPLY_VERB; - break; - case '>': - cur->type = REDIS_REPLY_PUSH; - break; - default: - __redisReaderSetErrorProtocolByte(r,*p); - return REDIS_ERR; - } - } else { - /* could not consume 1 byte */ - return REDIS_ERR; - } - } - - /* process typed item */ - switch(cur->type) { - case REDIS_REPLY_ERROR: - case REDIS_REPLY_STATUS: - case REDIS_REPLY_INTEGER: - case REDIS_REPLY_DOUBLE: - case REDIS_REPLY_NIL: - case REDIS_REPLY_BOOL: - return processLineItem(r); - case REDIS_REPLY_STRING: - case REDIS_REPLY_VERB: - return processBulkItem(r); - case REDIS_REPLY_ARRAY: - case REDIS_REPLY_MAP: - case REDIS_REPLY_SET: - case REDIS_REPLY_PUSH: - return processAggregateItem(r); - default: - assert(NULL); - return REDIS_ERR; /* Avoid warning. */ - } -} - -redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) { - redisReader *r; - - r = hi_calloc(1,sizeof(redisReader)); - if (r == NULL) - return NULL; - - r->buf = sdsempty(); - if (r->buf == NULL) - goto oom; - - r->task = hi_calloc(REDIS_READER_STACK_SIZE, sizeof(*r->task)); - if (r->task == NULL) - goto oom; - - for (; r->tasks < REDIS_READER_STACK_SIZE; r->tasks++) { - r->task[r->tasks] = hi_calloc(1, sizeof(**r->task)); - if (r->task[r->tasks] == NULL) - goto oom; - } - - r->fn = fn; - r->maxbuf = REDIS_READER_MAX_BUF; - r->maxelements = REDIS_READER_MAX_ARRAY_ELEMENTS; - r->ridx = -1; - - return r; -oom: - redisReaderFree(r); - return NULL; -} - -void redisReaderFree(redisReader *r) { - if (r == NULL) - return; - - if (r->reply != NULL && r->fn && r->fn->freeObject) - r->fn->freeObject(r->reply); - - if (r->task) { - /* We know r->task[i] is allocated if i < r->tasks */ - for (int i = 0; i < r->tasks; i++) { - hi_free(r->task[i]); - } - - hi_free(r->task); - } - - sdsfree(r->buf); - hi_free(r); -} - -int redisReaderFeed(redisReader *r, const char *buf, size_t len) { - sds newbuf; - - /* Return early when this reader is in an erroneous state. */ - if (r->err) - return REDIS_ERR; - - /* Copy the provided buffer. */ - if (buf != NULL && len >= 1) { - /* Destroy internal buffer when it is empty and is quite large. */ - if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) { - sdsfree(r->buf); - r->buf = sdsempty(); - if (r->buf == 0) goto oom; - - r->pos = 0; - } - - newbuf = sdscatlen(r->buf,buf,len); - if (newbuf == NULL) goto oom; - - r->buf = newbuf; - r->len = sdslen(r->buf); - } - - return REDIS_OK; -oom: - __redisReaderSetErrorOOM(r); - return REDIS_ERR; -} - -int redisReaderGetReply(redisReader *r, void **reply) { - /* Default target pointer to NULL. */ - if (reply != NULL) - *reply = NULL; - - /* Return early when this reader is in an erroneous state. */ - if (r->err) - return REDIS_ERR; - - /* When the buffer is empty, there will never be a reply. */ - if (r->len == 0) - return REDIS_OK; - - /* Set first item to process when the stack is empty. */ - if (r->ridx == -1) { - r->task[0]->type = -1; - r->task[0]->elements = -1; - r->task[0]->idx = -1; - r->task[0]->obj = NULL; - r->task[0]->parent = NULL; - r->task[0]->privdata = r->privdata; - r->ridx = 0; - } - - /* Process items in reply. */ - while (r->ridx >= 0) - if (processItem(r) != REDIS_OK) - break; - - /* Return ASAP when an error occurred. */ - if (r->err) - return REDIS_ERR; - - /* Discard part of the buffer when we've consumed at least 1k, to avoid - * doing unnecessary calls to memmove() in sds.c. */ - if (r->pos >= 1024) { - if (sdsrange(r->buf,r->pos,-1) < 0) return REDIS_ERR; - r->pos = 0; - r->len = sdslen(r->buf); - } - - /* Emit a reply when there is one. */ - if (r->ridx == -1) { - if (reply != NULL) { - *reply = r->reply; - } else if (r->reply != NULL && r->fn && r->fn->freeObject) { - r->fn->freeObject(r->reply); - } - r->reply = NULL; - } - return REDIS_OK; -} diff --git a/ext/hiredis-1.0.2/read.h b/ext/hiredis-1.0.2/read.h deleted file mode 100644 index 2d74d77a5..000000000 --- a/ext/hiredis-1.0.2/read.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - - -#ifndef __HIREDIS_READ_H -#define __HIREDIS_READ_H -#include /* for size_t */ - -#define REDIS_ERR -1 -#define REDIS_OK 0 - -/* When an error occurs, the err flag in a context is set to hold the type of - * error that occurred. REDIS_ERR_IO means there was an I/O error and you - * should use the "errno" variable to find out what is wrong. - * For other values, the "errstr" field will hold a description. */ -#define REDIS_ERR_IO 1 /* Error in read or write */ -#define REDIS_ERR_EOF 3 /* End of file */ -#define REDIS_ERR_PROTOCOL 4 /* Protocol error */ -#define REDIS_ERR_OOM 5 /* Out of memory */ -#define REDIS_ERR_TIMEOUT 6 /* Timed out */ -#define REDIS_ERR_OTHER 2 /* Everything else... */ - -#define REDIS_REPLY_STRING 1 -#define REDIS_REPLY_ARRAY 2 -#define REDIS_REPLY_INTEGER 3 -#define REDIS_REPLY_NIL 4 -#define REDIS_REPLY_STATUS 5 -#define REDIS_REPLY_ERROR 6 -#define REDIS_REPLY_DOUBLE 7 -#define REDIS_REPLY_BOOL 8 -#define REDIS_REPLY_MAP 9 -#define REDIS_REPLY_SET 10 -#define REDIS_REPLY_ATTR 11 -#define REDIS_REPLY_PUSH 12 -#define REDIS_REPLY_BIGNUM 13 -#define REDIS_REPLY_VERB 14 - -/* Default max unused reader buffer. */ -#define REDIS_READER_MAX_BUF (1024*16) - -/* Default multi-bulk element limit */ -#define REDIS_READER_MAX_ARRAY_ELEMENTS ((1LL<<32) - 1) - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct redisReadTask { - int type; - long long elements; /* number of elements in multibulk container */ - int idx; /* index in parent (array) object */ - void *obj; /* holds user-generated value for a read task */ - struct redisReadTask *parent; /* parent task */ - void *privdata; /* user-settable arbitrary field */ -} redisReadTask; - -typedef struct redisReplyObjectFunctions { - void *(*createString)(const redisReadTask*, char*, size_t); - void *(*createArray)(const redisReadTask*, size_t); - void *(*createInteger)(const redisReadTask*, long long); - void *(*createDouble)(const redisReadTask*, double, char*, size_t); - void *(*createNil)(const redisReadTask*); - void *(*createBool)(const redisReadTask*, int); - void (*freeObject)(void*); -} redisReplyObjectFunctions; - -typedef struct redisReader { - int err; /* Error flags, 0 when there is no error */ - char errstr[128]; /* String representation of error when applicable */ - - char *buf; /* Read buffer */ - size_t pos; /* Buffer cursor */ - size_t len; /* Buffer length */ - size_t maxbuf; /* Max length of unused buffer */ - long long maxelements; /* Max multi-bulk elements */ - - redisReadTask **task; - int tasks; - - int ridx; /* Index of current read task */ - void *reply; /* Temporary reply pointer */ - - redisReplyObjectFunctions *fn; - void *privdata; -} redisReader; - -/* Public API for the protocol parser. */ -redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn); -void redisReaderFree(redisReader *r); -int redisReaderFeed(redisReader *r, const char *buf, size_t len); -int redisReaderGetReply(redisReader *r, void **reply); - -#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p)) -#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply) -#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/ext/hiredis-1.0.2/sds.c b/ext/hiredis-1.0.2/sds.c deleted file mode 100644 index 49d2096b7..000000000 --- a/ext/hiredis-1.0.2/sds.c +++ /dev/null @@ -1,1289 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "fmacros.h" -#include -#include -#include -#include -#include -#include -#include "sds.h" -#include "sdsalloc.h" - -static inline int sdsHdrSize(char type) { - switch(type&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return sizeof(struct sdshdr5); - case SDS_TYPE_8: - return sizeof(struct sdshdr8); - case SDS_TYPE_16: - return sizeof(struct sdshdr16); - case SDS_TYPE_32: - return sizeof(struct sdshdr32); - case SDS_TYPE_64: - return sizeof(struct sdshdr64); - } - return 0; -} - -static inline char sdsReqType(size_t string_size) { - if (string_size < 32) - return SDS_TYPE_5; - if (string_size < 0xff) - return SDS_TYPE_8; - if (string_size < 0xffff) - return SDS_TYPE_16; - if (string_size < 0xffffffff) - return SDS_TYPE_32; - return SDS_TYPE_64; -} - -/* Create a new sds string with the content specified by the 'init' pointer - * and 'initlen'. - * If NULL is used for 'init' the string is initialized with zero bytes. - * - * The string is always null-termined (all the sds strings are, always) so - * even if you create an sds string with: - * - * mystring = sdsnewlen("abc",3); - * - * You can print the string with printf() as there is an implicit \0 at the - * end of the string. However the string is binary safe and can contain - * \0 characters in the middle, as the length is stored in the sds header. */ -sds sdsnewlen(const void *init, size_t initlen) { - void *sh; - sds s; - char type = sdsReqType(initlen); - /* Empty strings are usually created in order to append. Use type 8 - * since type 5 is not good at this. */ - if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8; - int hdrlen = sdsHdrSize(type); - unsigned char *fp; /* flags pointer. */ - - sh = s_malloc(hdrlen+initlen+1); - if (sh == NULL) return NULL; - if (!init) - memset(sh, 0, hdrlen+initlen+1); - s = (char*)sh+hdrlen; - fp = ((unsigned char*)s)-1; - switch(type) { - case SDS_TYPE_5: { - *fp = type | (initlen << SDS_TYPE_BITS); - break; - } - case SDS_TYPE_8: { - SDS_HDR_VAR(8,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - case SDS_TYPE_16: { - SDS_HDR_VAR(16,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - case SDS_TYPE_32: { - SDS_HDR_VAR(32,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - case SDS_TYPE_64: { - SDS_HDR_VAR(64,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - } - if (initlen && init) - memcpy(s, init, initlen); - s[initlen] = '\0'; - return s; -} - -/* Create an empty (zero length) sds string. Even in this case the string - * always has an implicit null term. */ -sds sdsempty(void) { - return sdsnewlen("",0); -} - -/* Create a new sds string starting from a null terminated C string. */ -sds sdsnew(const char *init) { - size_t initlen = (init == NULL) ? 0 : strlen(init); - return sdsnewlen(init, initlen); -} - -/* Duplicate an sds string. */ -sds sdsdup(const sds s) { - return sdsnewlen(s, sdslen(s)); -} - -/* Free an sds string. No operation is performed if 's' is NULL. */ -void sdsfree(sds s) { - if (s == NULL) return; - s_free((char*)s-sdsHdrSize(s[-1])); -} - -/* Set the sds string length to the length as obtained with strlen(), so - * considering as content only up to the first null term character. - * - * This function is useful when the sds string is hacked manually in some - * way, like in the following example: - * - * s = sdsnew("foobar"); - * s[2] = '\0'; - * sdsupdatelen(s); - * printf("%d\n", sdslen(s)); - * - * The output will be "2", but if we comment out the call to sdsupdatelen() - * the output will be "6" as the string was modified but the logical length - * remains 6 bytes. */ -void sdsupdatelen(sds s) { - int reallen = strlen(s); - sdssetlen(s, reallen); -} - -/* Modify an sds string in-place to make it empty (zero length). - * However all the existing buffer is not discarded but set as free space - * so that next append operations will not require allocations up to the - * number of bytes previously available. */ -void sdsclear(sds s) { - sdssetlen(s, 0); - s[0] = '\0'; -} - -/* Enlarge the free space at the end of the sds string so that the caller - * is sure that after calling this function can overwrite up to addlen - * bytes after the end of the string, plus one more byte for nul term. - * - * Note: this does not change the *length* of the sds string as returned - * by sdslen(), but only the free buffer space we have. */ -sds sdsMakeRoomFor(sds s, size_t addlen) { - void *sh, *newsh; - size_t avail = sdsavail(s); - size_t len, newlen; - char type, oldtype = s[-1] & SDS_TYPE_MASK; - int hdrlen; - - /* Return ASAP if there is enough space left. */ - if (avail >= addlen) return s; - - len = sdslen(s); - sh = (char*)s-sdsHdrSize(oldtype); - newlen = (len+addlen); - if (newlen < SDS_MAX_PREALLOC) - newlen *= 2; - else - newlen += SDS_MAX_PREALLOC; - - type = sdsReqType(newlen); - - /* Don't use type 5: the user is appending to the string and type 5 is - * not able to remember empty space, so sdsMakeRoomFor() must be called - * at every appending operation. */ - if (type == SDS_TYPE_5) type = SDS_TYPE_8; - - hdrlen = sdsHdrSize(type); - if (oldtype==type) { - newsh = s_realloc(sh, hdrlen+newlen+1); - if (newsh == NULL) return NULL; - s = (char*)newsh+hdrlen; - } else { - /* Since the header size changes, need to move the string forward, - * and can't use realloc */ - newsh = s_malloc(hdrlen+newlen+1); - if (newsh == NULL) return NULL; - memcpy((char*)newsh+hdrlen, s, len+1); - s_free(sh); - s = (char*)newsh+hdrlen; - s[-1] = type; - sdssetlen(s, len); - } - sdssetalloc(s, newlen); - return s; -} - -/* Reallocate the sds string so that it has no free space at the end. The - * contained string remains not altered, but next concatenation operations - * will require a reallocation. - * - * After the call, the passed sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdsRemoveFreeSpace(sds s) { - void *sh, *newsh; - char type, oldtype = s[-1] & SDS_TYPE_MASK; - int hdrlen; - size_t len = sdslen(s); - sh = (char*)s-sdsHdrSize(oldtype); - - type = sdsReqType(len); - hdrlen = sdsHdrSize(type); - if (oldtype==type) { - newsh = s_realloc(sh, hdrlen+len+1); - if (newsh == NULL) return NULL; - s = (char*)newsh+hdrlen; - } else { - newsh = s_malloc(hdrlen+len+1); - if (newsh == NULL) return NULL; - memcpy((char*)newsh+hdrlen, s, len+1); - s_free(sh); - s = (char*)newsh+hdrlen; - s[-1] = type; - sdssetlen(s, len); - } - sdssetalloc(s, len); - return s; -} - -/* Return the total size of the allocation of the specifed sds string, - * including: - * 1) The sds header before the pointer. - * 2) The string. - * 3) The free buffer at the end if any. - * 4) The implicit null term. - */ -size_t sdsAllocSize(sds s) { - size_t alloc = sdsalloc(s); - return sdsHdrSize(s[-1])+alloc+1; -} - -/* Return the pointer of the actual SDS allocation (normally SDS strings - * are referenced by the start of the string buffer). */ -void *sdsAllocPtr(sds s) { - return (void*) (s-sdsHdrSize(s[-1])); -} - -/* Increment the sds length and decrements the left free space at the - * end of the string according to 'incr'. Also set the null term - * in the new end of the string. - * - * This function is used in order to fix the string length after the - * user calls sdsMakeRoomFor(), writes something after the end of - * the current string, and finally needs to set the new length. - * - * Note: it is possible to use a negative increment in order to - * right-trim the string. - * - * Usage example: - * - * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the - * following schema, to cat bytes coming from the kernel to the end of an - * sds string without copying into an intermediate buffer: - * - * oldlen = sdslen(s); - * s = sdsMakeRoomFor(s, BUFFER_SIZE); - * nread = read(fd, s+oldlen, BUFFER_SIZE); - * ... check for nread <= 0 and handle it ... - * sdsIncrLen(s, nread); - */ -void sdsIncrLen(sds s, int incr) { - unsigned char flags = s[-1]; - size_t len; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: { - unsigned char *fp = ((unsigned char*)s)-1; - unsigned char oldlen = SDS_TYPE_5_LEN(flags); - assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr))); - *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS); - len = oldlen+incr; - break; - } - case SDS_TYPE_8: { - SDS_HDR_VAR(8,s); - assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); - len = (sh->len += incr); - break; - } - case SDS_TYPE_16: { - SDS_HDR_VAR(16,s); - assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); - len = (sh->len += incr); - break; - } - case SDS_TYPE_32: { - SDS_HDR_VAR(32,s); - assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); - len = (sh->len += incr); - break; - } - case SDS_TYPE_64: { - SDS_HDR_VAR(64,s); - assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr))); - len = (sh->len += incr); - break; - } - default: len = 0; /* Just to avoid compilation warnings. */ - } - s[len] = '\0'; -} - -/* Grow the sds to have the specified length. Bytes that were not part of - * the original length of the sds will be set to zero. - * - * if the specified length is smaller than the current length, no operation - * is performed. */ -sds sdsgrowzero(sds s, size_t len) { - size_t curlen = sdslen(s); - - if (len <= curlen) return s; - s = sdsMakeRoomFor(s,len-curlen); - if (s == NULL) return NULL; - - /* Make sure added region doesn't contain garbage */ - memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ - sdssetlen(s, len); - return s; -} - -/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the - * end of the specified sds string 's'. - * - * After the call, the passed sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscatlen(sds s, const void *t, size_t len) { - size_t curlen = sdslen(s); - - s = sdsMakeRoomFor(s,len); - if (s == NULL) return NULL; - memcpy(s+curlen, t, len); - sdssetlen(s, curlen+len); - s[curlen+len] = '\0'; - return s; -} - -/* Append the specified null termianted C string to the sds string 's'. - * - * After the call, the passed sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscat(sds s, const char *t) { - return sdscatlen(s, t, strlen(t)); -} - -/* Append the specified sds 't' to the existing sds 's'. - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscatsds(sds s, const sds t) { - return sdscatlen(s, t, sdslen(t)); -} - -/* Destructively modify the sds string 's' to hold the specified binary - * safe string pointed by 't' of length 'len' bytes. */ -sds sdscpylen(sds s, const char *t, size_t len) { - if (sdsalloc(s) < len) { - s = sdsMakeRoomFor(s,len-sdslen(s)); - if (s == NULL) return NULL; - } - memcpy(s, t, len); - s[len] = '\0'; - sdssetlen(s, len); - return s; -} - -/* Like sdscpylen() but 't' must be a null-termined string so that the length - * of the string is obtained with strlen(). */ -sds sdscpy(sds s, const char *t) { - return sdscpylen(s, t, strlen(t)); -} - -/* Helper for sdscatlonglong() doing the actual number -> string - * conversion. 's' must point to a string with room for at least - * SDS_LLSTR_SIZE bytes. - * - * The function returns the length of the null-terminated string - * representation stored at 's'. */ -#define SDS_LLSTR_SIZE 21 -int sdsll2str(char *s, long long value) { - char *p, aux; - unsigned long long v; - size_t l; - - /* Generate the string representation, this method produces - * an reversed string. */ - v = (value < 0) ? -value : value; - p = s; - do { - *p++ = '0'+(v%10); - v /= 10; - } while(v); - if (value < 0) *p++ = '-'; - - /* Compute length and add null term. */ - l = p-s; - *p = '\0'; - - /* Reverse the string. */ - p--; - while(s < p) { - aux = *s; - *s = *p; - *p = aux; - s++; - p--; - } - return l; -} - -/* Identical sdsll2str(), but for unsigned long long type. */ -int sdsull2str(char *s, unsigned long long v) { - char *p, aux; - size_t l; - - /* Generate the string representation, this method produces - * an reversed string. */ - p = s; - do { - *p++ = '0'+(v%10); - v /= 10; - } while(v); - - /* Compute length and add null term. */ - l = p-s; - *p = '\0'; - - /* Reverse the string. */ - p--; - while(s < p) { - aux = *s; - *s = *p; - *p = aux; - s++; - p--; - } - return l; -} - -/* Create an sds string from a long long value. It is much faster than: - * - * sdscatprintf(sdsempty(),"%lld\n", value); - */ -sds sdsfromlonglong(long long value) { - char buf[SDS_LLSTR_SIZE]; - int len = sdsll2str(buf,value); - - return sdsnewlen(buf,len); -} - -/* Like sdscatprintf() but gets va_list instead of being variadic. */ -sds sdscatvprintf(sds s, const char *fmt, va_list ap) { - va_list cpy; - char staticbuf[1024], *buf = staticbuf, *t; - size_t buflen = strlen(fmt)*2; - - /* We try to start using a static buffer for speed. - * If not possible we revert to heap allocation. */ - if (buflen > sizeof(staticbuf)) { - buf = s_malloc(buflen); - if (buf == NULL) return NULL; - } else { - buflen = sizeof(staticbuf); - } - - /* Try with buffers two times bigger every time we fail to - * fit the string in the current buffer size. */ - while(1) { - buf[buflen-2] = '\0'; - va_copy(cpy,ap); - vsnprintf(buf, buflen, fmt, cpy); - va_end(cpy); - if (buf[buflen-2] != '\0') { - if (buf != staticbuf) s_free(buf); - buflen *= 2; - buf = s_malloc(buflen); - if (buf == NULL) return NULL; - continue; - } - break; - } - - /* Finally concat the obtained string to the SDS string and return it. */ - t = sdscat(s, buf); - if (buf != staticbuf) s_free(buf); - return t; -} - -/* Append to the sds string 's' a string obtained using printf-alike format - * specifier. - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. - * - * Example: - * - * s = sdsnew("Sum is: "); - * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b). - * - * Often you need to create a string from scratch with the printf-alike - * format. When this is the need, just use sdsempty() as the target string: - * - * s = sdscatprintf(sdsempty(), "... your format ...", args); - */ -sds sdscatprintf(sds s, const char *fmt, ...) { - va_list ap; - char *t; - va_start(ap, fmt); - t = sdscatvprintf(s,fmt,ap); - va_end(ap); - return t; -} - -/* This function is similar to sdscatprintf, but much faster as it does - * not rely on sprintf() family functions implemented by the libc that - * are often very slow. Moreover directly handling the sds string as - * new data is concatenated provides a performance improvement. - * - * However this function only handles an incompatible subset of printf-alike - * format specifiers: - * - * %s - C String - * %S - SDS string - * %i - signed int - * %I - 64 bit signed integer (long long, int64_t) - * %u - unsigned int - * %U - 64 bit unsigned integer (unsigned long long, uint64_t) - * %% - Verbatim "%" character. - */ -sds sdscatfmt(sds s, char const *fmt, ...) { - const char *f = fmt; - int i; - va_list ap; - - va_start(ap,fmt); - i = sdslen(s); /* Position of the next byte to write to dest str. */ - while(*f) { - char next, *str; - size_t l; - long long num; - unsigned long long unum; - - /* Make sure there is always space for at least 1 char. */ - if (sdsavail(s)==0) { - s = sdsMakeRoomFor(s,1); - if (s == NULL) goto fmt_error; - } - - switch(*f) { - case '%': - next = *(f+1); - f++; - switch(next) { - case 's': - case 'S': - str = va_arg(ap,char*); - l = (next == 's') ? strlen(str) : sdslen(str); - if (sdsavail(s) < l) { - s = sdsMakeRoomFor(s,l); - if (s == NULL) goto fmt_error; - } - memcpy(s+i,str,l); - sdsinclen(s,l); - i += l; - break; - case 'i': - case 'I': - if (next == 'i') - num = va_arg(ap,int); - else - num = va_arg(ap,long long); - { - char buf[SDS_LLSTR_SIZE]; - l = sdsll2str(buf,num); - if (sdsavail(s) < l) { - s = sdsMakeRoomFor(s,l); - if (s == NULL) goto fmt_error; - } - memcpy(s+i,buf,l); - sdsinclen(s,l); - i += l; - } - break; - case 'u': - case 'U': - if (next == 'u') - unum = va_arg(ap,unsigned int); - else - unum = va_arg(ap,unsigned long long); - { - char buf[SDS_LLSTR_SIZE]; - l = sdsull2str(buf,unum); - if (sdsavail(s) < l) { - s = sdsMakeRoomFor(s,l); - if (s == NULL) goto fmt_error; - } - memcpy(s+i,buf,l); - sdsinclen(s,l); - i += l; - } - break; - default: /* Handle %% and generally %. */ - s[i++] = next; - sdsinclen(s,1); - break; - } - break; - default: - s[i++] = *f; - sdsinclen(s,1); - break; - } - f++; - } - va_end(ap); - - /* Add null-term */ - s[i] = '\0'; - return s; - -fmt_error: - va_end(ap); - return NULL; -} - -/* Remove the part of the string from left and from right composed just of - * contiguous characters found in 'cset', that is a null terminted C string. - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. - * - * Example: - * - * s = sdsnew("AA...AA.a.aa.aHelloWorld :::"); - * s = sdstrim(s,"Aa. :"); - * printf("%s\n", s); - * - * Output will be just "Hello World". - */ -sds sdstrim(sds s, const char *cset) { - char *start, *end, *sp, *ep; - size_t len; - - sp = start = s; - ep = end = s+sdslen(s)-1; - while(sp <= end && strchr(cset, *sp)) sp++; - while(ep > sp && strchr(cset, *ep)) ep--; - len = (sp > ep) ? 0 : ((ep-sp)+1); - if (s != sp) memmove(s, sp, len); - s[len] = '\0'; - sdssetlen(s,len); - return s; -} - -/* Turn the string into a smaller (or equal) string containing only the - * substring specified by the 'start' and 'end' indexes. - * - * start and end can be negative, where -1 means the last character of the - * string, -2 the penultimate character, and so forth. - * - * The interval is inclusive, so the start and end characters will be part - * of the resulting string. - * - * The string is modified in-place. - * - * Return value: - * -1 (error) if sdslen(s) is larger than maximum positive ssize_t value. - * 0 on success. - * - * Example: - * - * s = sdsnew("Hello World"); - * sdsrange(s,1,-1); => "ello World" - */ -int sdsrange(sds s, ssize_t start, ssize_t end) { - size_t newlen, len = sdslen(s); - if (len > SSIZE_MAX) return -1; - - if (len == 0) return 0; - if (start < 0) { - start = len+start; - if (start < 0) start = 0; - } - if (end < 0) { - end = len+end; - if (end < 0) end = 0; - } - newlen = (start > end) ? 0 : (end-start)+1; - if (newlen != 0) { - if (start >= (ssize_t)len) { - newlen = 0; - } else if (end >= (ssize_t)len) { - end = len-1; - newlen = (start > end) ? 0 : (end-start)+1; - } - } else { - start = 0; - } - if (start && newlen) memmove(s, s+start, newlen); - s[newlen] = 0; - sdssetlen(s,newlen); - return 0; -} - -/* Apply tolower() to every character of the sds string 's'. */ -void sdstolower(sds s) { - int len = sdslen(s), j; - - for (j = 0; j < len; j++) s[j] = tolower(s[j]); -} - -/* Apply toupper() to every character of the sds string 's'. */ -void sdstoupper(sds s) { - int len = sdslen(s), j; - - for (j = 0; j < len; j++) s[j] = toupper(s[j]); -} - -/* Compare two sds strings s1 and s2 with memcmp(). - * - * Return value: - * - * positive if s1 > s2. - * negative if s1 < s2. - * 0 if s1 and s2 are exactly the same binary string. - * - * If two strings share exactly the same prefix, but one of the two has - * additional characters, the longer string is considered to be greater than - * the smaller one. */ -int sdscmp(const sds s1, const sds s2) { - size_t l1, l2, minlen; - int cmp; - - l1 = sdslen(s1); - l2 = sdslen(s2); - minlen = (l1 < l2) ? l1 : l2; - cmp = memcmp(s1,s2,minlen); - if (cmp == 0) return l1-l2; - return cmp; -} - -/* Split 's' with separator in 'sep'. An array - * of sds strings is returned. *count will be set - * by reference to the number of tokens returned. - * - * On out of memory, zero length string, zero length - * separator, NULL is returned. - * - * Note that 'sep' is able to split a string using - * a multi-character separator. For example - * sdssplit("foo_-_bar","_-_"); will return two - * elements "foo" and "bar". - * - * This version of the function is binary-safe but - * requires length arguments. sdssplit() is just the - * same function but for zero-terminated strings. - */ -sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) { - int elements = 0, slots = 5, start = 0, j; - sds *tokens; - - if (seplen < 1 || len < 0) return NULL; - - tokens = s_malloc(sizeof(sds)*slots); - if (tokens == NULL) return NULL; - - if (len == 0) { - *count = 0; - return tokens; - } - for (j = 0; j < (len-(seplen-1)); j++) { - /* make sure there is room for the next element and the final one */ - if (slots < elements+2) { - sds *newtokens; - - slots *= 2; - newtokens = s_realloc(tokens,sizeof(sds)*slots); - if (newtokens == NULL) goto cleanup; - tokens = newtokens; - } - /* search the separator */ - if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) { - tokens[elements] = sdsnewlen(s+start,j-start); - if (tokens[elements] == NULL) goto cleanup; - elements++; - start = j+seplen; - j = j+seplen-1; /* skip the separator */ - } - } - /* Add the final element. We are sure there is room in the tokens array. */ - tokens[elements] = sdsnewlen(s+start,len-start); - if (tokens[elements] == NULL) goto cleanup; - elements++; - *count = elements; - return tokens; - -cleanup: - { - int i; - for (i = 0; i < elements; i++) sdsfree(tokens[i]); - s_free(tokens); - *count = 0; - return NULL; - } -} - -/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */ -void sdsfreesplitres(sds *tokens, int count) { - if (!tokens) return; - while(count--) - sdsfree(tokens[count]); - s_free(tokens); -} - -/* Append to the sds string "s" an escaped string representation where - * all the non-printable characters (tested with isprint()) are turned into - * escapes in the form "\n\r\a...." or "\x". - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscatrepr(sds s, const char *p, size_t len) { - s = sdscatlen(s,"\"",1); - while(len--) { - switch(*p) { - case '\\': - case '"': - s = sdscatprintf(s,"\\%c",*p); - break; - case '\n': s = sdscatlen(s,"\\n",2); break; - case '\r': s = sdscatlen(s,"\\r",2); break; - case '\t': s = sdscatlen(s,"\\t",2); break; - case '\a': s = sdscatlen(s,"\\a",2); break; - case '\b': s = sdscatlen(s,"\\b",2); break; - default: - if (isprint(*p)) - s = sdscatprintf(s,"%c",*p); - else - s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); - break; - } - p++; - } - return sdscatlen(s,"\"",1); -} - -/* Helper function for sdssplitargs() that converts a hex digit into an - * integer from 0 to 15 */ -int hex_digit_to_int(char c) { - switch(c) { - case '0': return 0; - case '1': return 1; - case '2': return 2; - case '3': return 3; - case '4': return 4; - case '5': return 5; - case '6': return 6; - case '7': return 7; - case '8': return 8; - case '9': return 9; - case 'a': case 'A': return 10; - case 'b': case 'B': return 11; - case 'c': case 'C': return 12; - case 'd': case 'D': return 13; - case 'e': case 'E': return 14; - case 'f': case 'F': return 15; - default: return 0; - } -} - -/* Split a line into arguments, where every argument can be in the - * following programming-language REPL-alike form: - * - * foo bar "newline are supported\n" and "\xff\x00otherstuff" - * - * The number of arguments is stored into *argc, and an array - * of sds is returned. - * - * The caller should free the resulting array of sds strings with - * sdsfreesplitres(). - * - * Note that sdscatrepr() is able to convert back a string into - * a quoted string in the same format sdssplitargs() is able to parse. - * - * The function returns the allocated tokens on success, even when the - * input string is empty, or NULL if the input contains unbalanced - * quotes or closed quotes followed by non space characters - * as in: "foo"bar or "foo' - */ -sds *sdssplitargs(const char *line, int *argc) { - const char *p = line; - char *current = NULL; - char **vector = NULL; - - *argc = 0; - while(1) { - /* skip blanks */ - while(*p && isspace(*p)) p++; - if (*p) { - /* get a token */ - int inq=0; /* set to 1 if we are in "quotes" */ - int insq=0; /* set to 1 if we are in 'single quotes' */ - int done=0; - - if (current == NULL) current = sdsempty(); - while(!done) { - if (inq) { - if (*p == '\\' && *(p+1) == 'x' && - isxdigit(*(p+2)) && - isxdigit(*(p+3))) - { - unsigned char byte; - - byte = (hex_digit_to_int(*(p+2))*16)+ - hex_digit_to_int(*(p+3)); - current = sdscatlen(current,(char*)&byte,1); - p += 3; - } else if (*p == '\\' && *(p+1)) { - char c; - - p++; - switch(*p) { - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'b': c = '\b'; break; - case 'a': c = '\a'; break; - default: c = *p; break; - } - current = sdscatlen(current,&c,1); - } else if (*p == '"') { - /* closing quote must be followed by a space or - * nothing at all. */ - if (*(p+1) && !isspace(*(p+1))) goto err; - done=1; - } else if (!*p) { - /* unterminated quotes */ - goto err; - } else { - current = sdscatlen(current,p,1); - } - } else if (insq) { - if (*p == '\\' && *(p+1) == '\'') { - p++; - current = sdscatlen(current,"'",1); - } else if (*p == '\'') { - /* closing quote must be followed by a space or - * nothing at all. */ - if (*(p+1) && !isspace(*(p+1))) goto err; - done=1; - } else if (!*p) { - /* unterminated quotes */ - goto err; - } else { - current = sdscatlen(current,p,1); - } - } else { - switch(*p) { - case ' ': - case '\n': - case '\r': - case '\t': - case '\0': - done=1; - break; - case '"': - inq=1; - break; - case '\'': - insq=1; - break; - default: - current = sdscatlen(current,p,1); - break; - } - } - if (*p) p++; - } - /* add the token to the vector */ - { - char **new_vector = s_realloc(vector,((*argc)+1)*sizeof(char*)); - if (new_vector == NULL) { - s_free(vector); - return NULL; - } - - vector = new_vector; - vector[*argc] = current; - (*argc)++; - current = NULL; - } - } else { - /* Even on empty input string return something not NULL. */ - if (vector == NULL) vector = s_malloc(sizeof(void*)); - return vector; - } - } - -err: - while((*argc)--) - sdsfree(vector[*argc]); - s_free(vector); - if (current) sdsfree(current); - *argc = 0; - return NULL; -} - -/* Modify the string substituting all the occurrences of the set of - * characters specified in the 'from' string to the corresponding character - * in the 'to' array. - * - * For instance: sdsmapchars(mystring, "ho", "01", 2) - * will have the effect of turning the string "hello" into "0ell1". - * - * The function returns the sds string pointer, that is always the same - * as the input pointer since no resize is needed. */ -sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) { - size_t j, i, l = sdslen(s); - - for (j = 0; j < l; j++) { - for (i = 0; i < setlen; i++) { - if (s[j] == from[i]) { - s[j] = to[i]; - break; - } - } - } - return s; -} - -/* Join an array of C strings using the specified separator (also a C string). - * Returns the result as an sds string. */ -sds sdsjoin(char **argv, int argc, char *sep) { - sds join = sdsempty(); - int j; - - for (j = 0; j < argc; j++) { - join = sdscat(join, argv[j]); - if (j != argc-1) join = sdscat(join,sep); - } - return join; -} - -/* Like sdsjoin, but joins an array of SDS strings. */ -sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) { - sds join = sdsempty(); - int j; - - for (j = 0; j < argc; j++) { - join = sdscatsds(join, argv[j]); - if (j != argc-1) join = sdscatlen(join,sep,seplen); - } - return join; -} - -/* Wrappers to the allocators used by SDS. Note that SDS will actually - * just use the macros defined into sdsalloc.h in order to avoid to pay - * the overhead of function calls. Here we define these wrappers only for - * the programs SDS is linked to, if they want to touch the SDS internals - * even if they use a different allocator. */ -void *sds_malloc(size_t size) { return s_malloc(size); } -void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); } -void sds_free(void *ptr) { s_free(ptr); } - -#if defined(SDS_TEST_MAIN) -#include -#include "testhelp.h" -#include "limits.h" - -#define UNUSED(x) (void)(x) -int sdsTest(void) { - { - sds x = sdsnew("foo"), y; - - test_cond("Create a string and obtain the length", - sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) - - sdsfree(x); - x = sdsnewlen("foo",2); - test_cond("Create a string with specified length", - sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) - - x = sdscat(x,"bar"); - test_cond("Strings concatenation", - sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); - - x = sdscpy(x,"a"); - test_cond("sdscpy() against an originally longer string", - sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) - - x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); - test_cond("sdscpy() against an originally shorter string", - sdslen(x) == 33 && - memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) - - sdsfree(x); - x = sdscatprintf(sdsempty(),"%d",123); - test_cond("sdscatprintf() seems working in the base case", - sdslen(x) == 3 && memcmp(x,"123\0",4) == 0) - - sdsfree(x); - x = sdsnew("--"); - x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX); - test_cond("sdscatfmt() seems working in the base case", - sdslen(x) == 60 && - memcmp(x,"--Hello Hi! World -9223372036854775808," - "9223372036854775807--",60) == 0) - printf("[%s]\n",x); - - sdsfree(x); - x = sdsnew("--"); - x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX); - test_cond("sdscatfmt() seems working with unsigned numbers", - sdslen(x) == 35 && - memcmp(x,"--4294967295,18446744073709551615--",35) == 0) - - sdsfree(x); - x = sdsnew(" x "); - sdstrim(x," x"); - test_cond("sdstrim() works when all chars match", - sdslen(x) == 0) - - sdsfree(x); - x = sdsnew(" x "); - sdstrim(x," "); - test_cond("sdstrim() works when a single char remains", - sdslen(x) == 1 && x[0] == 'x') - - sdsfree(x); - x = sdsnew("xxciaoyyy"); - sdstrim(x,"xy"); - test_cond("sdstrim() correctly trims characters", - sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) - - y = sdsdup(x); - sdsrange(y,1,1); - test_cond("sdsrange(...,1,1)", - sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,1,-1); - test_cond("sdsrange(...,1,-1)", - sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,-2,-1); - test_cond("sdsrange(...,-2,-1)", - sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,2,1); - test_cond("sdsrange(...,2,1)", - sdslen(y) == 0 && memcmp(y,"\0",1) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,1,100); - test_cond("sdsrange(...,1,100)", - sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,100,100); - test_cond("sdsrange(...,100,100)", - sdslen(y) == 0 && memcmp(y,"\0",1) == 0) - - sdsfree(y); - sdsfree(x); - x = sdsnew("foo"); - y = sdsnew("foa"); - test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) - - sdsfree(y); - sdsfree(x); - x = sdsnew("bar"); - y = sdsnew("bar"); - test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) - - sdsfree(y); - sdsfree(x); - x = sdsnew("aar"); - y = sdsnew("bar"); - test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) - - sdsfree(y); - sdsfree(x); - x = sdsnewlen("\a\n\0foo\r",7); - y = sdscatrepr(sdsempty(),x,sdslen(x)); - test_cond("sdscatrepr(...data...)", - memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0) - - { - unsigned int oldfree; - char *p; - int step = 10, j, i; - - sdsfree(x); - sdsfree(y); - x = sdsnew("0"); - test_cond("sdsnew() free/len buffers", sdslen(x) == 1 && sdsavail(x) == 0); - - /* Run the test a few times in order to hit the first two - * SDS header types. */ - for (i = 0; i < 10; i++) { - int oldlen = sdslen(x); - x = sdsMakeRoomFor(x,step); - int type = x[-1]&SDS_TYPE_MASK; - - test_cond("sdsMakeRoomFor() len", sdslen(x) == oldlen); - if (type != SDS_TYPE_5) { - test_cond("sdsMakeRoomFor() free", sdsavail(x) >= step); - oldfree = sdsavail(x); - } - p = x+oldlen; - for (j = 0; j < step; j++) { - p[j] = 'A'+j; - } - sdsIncrLen(x,step); - } - test_cond("sdsMakeRoomFor() content", - memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ",x,101) == 0); - test_cond("sdsMakeRoomFor() final length",sdslen(x)==101); - - sdsfree(x); - } - } - test_report() - return 0; -} -#endif - -#ifdef SDS_TEST_MAIN -int main(void) { - return sdsTest(); -} -#endif diff --git a/ext/hiredis-1.0.2/sds.h b/ext/hiredis-1.0.2/sds.h deleted file mode 100644 index eda8833b5..000000000 --- a/ext/hiredis-1.0.2/sds.h +++ /dev/null @@ -1,278 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __SDS_H -#define __SDS_H - -#define SDS_MAX_PREALLOC (1024*1024) -#ifdef _MSC_VER -#define __attribute__(x) -typedef long long ssize_t; -#define SSIZE_MAX (LLONG_MAX >> 1) -#endif - -#include -#include -#include - -typedef char *sds; - -/* Note: sdshdr5 is never used, we just access the flags byte directly. - * However is here to document the layout of type 5 SDS strings. */ -struct __attribute__ ((__packed__)) sdshdr5 { - unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr8 { - uint8_t len; /* used */ - uint8_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr16 { - uint16_t len; /* used */ - uint16_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr32 { - uint32_t len; /* used */ - uint32_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr64 { - uint64_t len; /* used */ - uint64_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; - -#define SDS_TYPE_5 0 -#define SDS_TYPE_8 1 -#define SDS_TYPE_16 2 -#define SDS_TYPE_32 3 -#define SDS_TYPE_64 4 -#define SDS_TYPE_MASK 7 -#define SDS_TYPE_BITS 3 -#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))); -#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) -#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) - -static inline size_t sdslen(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return SDS_TYPE_5_LEN(flags); - case SDS_TYPE_8: - return SDS_HDR(8,s)->len; - case SDS_TYPE_16: - return SDS_HDR(16,s)->len; - case SDS_TYPE_32: - return SDS_HDR(32,s)->len; - case SDS_TYPE_64: - return SDS_HDR(64,s)->len; - } - return 0; -} - -static inline size_t sdsavail(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: { - return 0; - } - case SDS_TYPE_8: { - SDS_HDR_VAR(8,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_16: { - SDS_HDR_VAR(16,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_32: { - SDS_HDR_VAR(32,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_64: { - SDS_HDR_VAR(64,s); - return sh->alloc - sh->len; - } - } - return 0; -} - -static inline void sdssetlen(sds s, size_t newlen) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - { - unsigned char *fp = ((unsigned char*)s)-1; - *fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS)); - } - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->len = (uint8_t)newlen; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->len = (uint16_t)newlen; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->len = (uint32_t)newlen; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->len = (uint64_t)newlen; - break; - } -} - -static inline void sdsinclen(sds s, size_t inc) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - { - unsigned char *fp = ((unsigned char*)s)-1; - unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc; - *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); - } - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->len += (uint8_t)inc; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->len += (uint16_t)inc; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->len += (uint32_t)inc; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->len += (uint64_t)inc; - break; - } -} - -/* sdsalloc() = sdsavail() + sdslen() */ -static inline size_t sdsalloc(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return SDS_TYPE_5_LEN(flags); - case SDS_TYPE_8: - return SDS_HDR(8,s)->alloc; - case SDS_TYPE_16: - return SDS_HDR(16,s)->alloc; - case SDS_TYPE_32: - return SDS_HDR(32,s)->alloc; - case SDS_TYPE_64: - return SDS_HDR(64,s)->alloc; - } - return 0; -} - -static inline void sdssetalloc(sds s, size_t newlen) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - /* Nothing to do, this type has no total allocation info. */ - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->alloc = (uint8_t)newlen; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->alloc = (uint16_t)newlen; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->alloc = (uint32_t)newlen; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->alloc = (uint64_t)newlen; - break; - } -} - -sds sdsnewlen(const void *init, size_t initlen); -sds sdsnew(const char *init); -sds sdsempty(void); -sds sdsdup(const sds s); -void sdsfree(sds s); -sds sdsgrowzero(sds s, size_t len); -sds sdscatlen(sds s, const void *t, size_t len); -sds sdscat(sds s, const char *t); -sds sdscatsds(sds s, const sds t); -sds sdscpylen(sds s, const char *t, size_t len); -sds sdscpy(sds s, const char *t); - -sds sdscatvprintf(sds s, const char *fmt, va_list ap); -#ifdef __GNUC__ -sds sdscatprintf(sds s, const char *fmt, ...) - __attribute__((format(printf, 2, 3))); -#else -sds sdscatprintf(sds s, const char *fmt, ...); -#endif - -sds sdscatfmt(sds s, char const *fmt, ...); -sds sdstrim(sds s, const char *cset); -int sdsrange(sds s, ssize_t start, ssize_t end); -void sdsupdatelen(sds s); -void sdsclear(sds s); -int sdscmp(const sds s1, const sds s2); -sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); -void sdsfreesplitres(sds *tokens, int count); -void sdstolower(sds s); -void sdstoupper(sds s); -sds sdsfromlonglong(long long value); -sds sdscatrepr(sds s, const char *p, size_t len); -sds *sdssplitargs(const char *line, int *argc); -sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); -sds sdsjoin(char **argv, int argc, char *sep); -sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); - -/* Low level functions exposed to the user API */ -sds sdsMakeRoomFor(sds s, size_t addlen); -void sdsIncrLen(sds s, int incr); -sds sdsRemoveFreeSpace(sds s); -size_t sdsAllocSize(sds s); -void *sdsAllocPtr(sds s); - -/* Export the allocator used by SDS to the program using SDS. - * Sometimes the program SDS is linked to, may use a different set of - * allocators, but may want to allocate or free things that SDS will - * respectively free or allocate. */ -void *sds_malloc(size_t size); -void *sds_realloc(void *ptr, size_t size); -void sds_free(void *ptr); - -#ifdef REDIS_TEST -int sdsTest(int argc, char *argv[]); -#endif - -#endif diff --git a/ext/hiredis-1.0.2/sdsalloc.h b/ext/hiredis-1.0.2/sdsalloc.h deleted file mode 100644 index 5538dd94c..000000000 --- a/ext/hiredis-1.0.2/sdsalloc.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/* SDS allocator selection. - * - * This file is used in order to change the SDS allocator at compile time. - * Just define the following defines to what you want to use. Also add - * the include of your alternate allocator if needed (not needed in order - * to use the default libc allocator). */ - -#include "alloc.h" - -#define s_malloc hi_malloc -#define s_realloc hi_realloc -#define s_free hi_free diff --git a/ext/hiredis-1.0.2/sockcompat.c b/ext/hiredis-1.0.2/sockcompat.c deleted file mode 100644 index f99d14b05..000000000 --- a/ext/hiredis-1.0.2/sockcompat.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2019, Marcus Geelnard - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#define REDIS_SOCKCOMPAT_IMPLEMENTATION -#include "sockcompat.h" - -#ifdef _WIN32 -static int _wsaErrorToErrno(int err) { - switch (err) { - case WSAEWOULDBLOCK: - return EWOULDBLOCK; - case WSAEINPROGRESS: - return EINPROGRESS; - case WSAEALREADY: - return EALREADY; - case WSAENOTSOCK: - return ENOTSOCK; - case WSAEDESTADDRREQ: - return EDESTADDRREQ; - case WSAEMSGSIZE: - return EMSGSIZE; - case WSAEPROTOTYPE: - return EPROTOTYPE; - case WSAENOPROTOOPT: - return ENOPROTOOPT; - case WSAEPROTONOSUPPORT: - return EPROTONOSUPPORT; - case WSAEOPNOTSUPP: - return EOPNOTSUPP; - case WSAEAFNOSUPPORT: - return EAFNOSUPPORT; - case WSAEADDRINUSE: - return EADDRINUSE; - case WSAEADDRNOTAVAIL: - return EADDRNOTAVAIL; - case WSAENETDOWN: - return ENETDOWN; - case WSAENETUNREACH: - return ENETUNREACH; - case WSAENETRESET: - return ENETRESET; - case WSAECONNABORTED: - return ECONNABORTED; - case WSAECONNRESET: - return ECONNRESET; - case WSAENOBUFS: - return ENOBUFS; - case WSAEISCONN: - return EISCONN; - case WSAENOTCONN: - return ENOTCONN; - case WSAETIMEDOUT: - return ETIMEDOUT; - case WSAECONNREFUSED: - return ECONNREFUSED; - case WSAELOOP: - return ELOOP; - case WSAENAMETOOLONG: - return ENAMETOOLONG; - case WSAEHOSTUNREACH: - return EHOSTUNREACH; - case WSAENOTEMPTY: - return ENOTEMPTY; - default: - /* We just return a generic I/O error if we could not find a relevant error. */ - return EIO; - } -} - -static void _updateErrno(int success) { - errno = success ? 0 : _wsaErrorToErrno(WSAGetLastError()); -} - -static int _initWinsock() { - static int s_initialized = 0; - if (!s_initialized) { - static WSADATA wsadata; - int err = WSAStartup(MAKEWORD(2,2), &wsadata); - if (err != 0) { - errno = _wsaErrorToErrno(err); - return 0; - } - s_initialized = 1; - } - return 1; -} - -int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { - /* Note: This function is likely to be called before other functions, so run init here. */ - if (!_initWinsock()) { - return EAI_FAIL; - } - - switch (getaddrinfo(node, service, hints, res)) { - case 0: return 0; - case WSATRY_AGAIN: return EAI_AGAIN; - case WSAEINVAL: return EAI_BADFLAGS; - case WSAEAFNOSUPPORT: return EAI_FAMILY; - case WSA_NOT_ENOUGH_MEMORY: return EAI_MEMORY; - case WSAHOST_NOT_FOUND: return EAI_NONAME; - case WSATYPE_NOT_FOUND: return EAI_SERVICE; - case WSAESOCKTNOSUPPORT: return EAI_SOCKTYPE; - default: return EAI_FAIL; /* Including WSANO_RECOVERY */ - } -} - -const char *win32_gai_strerror(int errcode) { - switch (errcode) { - case 0: errcode = 0; break; - case EAI_AGAIN: errcode = WSATRY_AGAIN; break; - case EAI_BADFLAGS: errcode = WSAEINVAL; break; - case EAI_FAMILY: errcode = WSAEAFNOSUPPORT; break; - case EAI_MEMORY: errcode = WSA_NOT_ENOUGH_MEMORY; break; - case EAI_NONAME: errcode = WSAHOST_NOT_FOUND; break; - case EAI_SERVICE: errcode = WSATYPE_NOT_FOUND; break; - case EAI_SOCKTYPE: errcode = WSAESOCKTNOSUPPORT; break; - default: errcode = WSANO_RECOVERY; break; /* Including EAI_FAIL */ - } - return gai_strerror(errcode); -} - -void win32_freeaddrinfo(struct addrinfo *res) { - freeaddrinfo(res); -} - -SOCKET win32_socket(int domain, int type, int protocol) { - SOCKET s; - - /* Note: This function is likely to be called before other functions, so run init here. */ - if (!_initWinsock()) { - return INVALID_SOCKET; - } - - _updateErrno((s = socket(domain, type, protocol)) != INVALID_SOCKET); - return s; -} - -int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp) { - int ret = ioctlsocket(fd, (long)request, argp); - _updateErrno(ret != SOCKET_ERROR); - return ret != SOCKET_ERROR ? ret : -1; -} - -int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) { - int ret = bind(sockfd, addr, addrlen); - _updateErrno(ret != SOCKET_ERROR); - return ret != SOCKET_ERROR ? ret : -1; -} - -int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) { - int ret = connect(sockfd, addr, addrlen); - _updateErrno(ret != SOCKET_ERROR); - - /* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as - * EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX - * logic consistent. */ - if (errno == EWOULDBLOCK) { - errno = EINPROGRESS; - } - - return ret != SOCKET_ERROR ? ret : -1; -} - -int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen) { - int ret = 0; - if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) { - if (*optlen >= sizeof (struct timeval)) { - struct timeval *tv = optval; - DWORD timeout = 0; - socklen_t dwlen = 0; - ret = getsockopt(sockfd, level, optname, (char *)&timeout, &dwlen); - tv->tv_sec = timeout / 1000; - tv->tv_usec = (timeout * 1000) % 1000000; - } else { - ret = WSAEFAULT; - } - *optlen = sizeof (struct timeval); - } else { - ret = getsockopt(sockfd, level, optname, (char*)optval, optlen); - } - _updateErrno(ret != SOCKET_ERROR); - return ret != SOCKET_ERROR ? ret : -1; -} - -int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen) { - int ret = 0; - if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) { - const struct timeval *tv = optval; - DWORD timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000; - ret = setsockopt(sockfd, level, optname, (const char*)&timeout, sizeof(DWORD)); - } else { - ret = setsockopt(sockfd, level, optname, (const char*)optval, optlen); - } - _updateErrno(ret != SOCKET_ERROR); - return ret != SOCKET_ERROR ? ret : -1; -} - -int win32_close(SOCKET fd) { - int ret = closesocket(fd); - _updateErrno(ret != SOCKET_ERROR); - return ret != SOCKET_ERROR ? ret : -1; -} - -ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags) { - int ret = recv(sockfd, (char*)buf, (int)len, flags); - _updateErrno(ret != SOCKET_ERROR); - return ret != SOCKET_ERROR ? ret : -1; -} - -ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags) { - int ret = send(sockfd, (const char*)buf, (int)len, flags); - _updateErrno(ret != SOCKET_ERROR); - return ret != SOCKET_ERROR ? ret : -1; -} - -int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) { - int ret = WSAPoll(fds, nfds, timeout); - _updateErrno(ret != SOCKET_ERROR); - return ret != SOCKET_ERROR ? ret : -1; -} -#endif /* _WIN32 */ diff --git a/ext/hiredis-1.0.2/sockcompat.h b/ext/hiredis-1.0.2/sockcompat.h deleted file mode 100644 index 85810e848..000000000 --- a/ext/hiredis-1.0.2/sockcompat.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2019, Marcus Geelnard - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __SOCKCOMPAT_H -#define __SOCKCOMPAT_H - -#ifndef _WIN32 -/* For POSIX systems we use the standard BSD socket API. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#else -/* For Windows we use winsock. */ -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 /* To get WSAPoll etc. */ -#include -#include -#include -#include - -#ifdef _MSC_VER -typedef long long ssize_t; -#endif - -/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */ -int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); -const char *win32_gai_strerror(int errcode); -void win32_freeaddrinfo(struct addrinfo *res); -SOCKET win32_socket(int domain, int type, int protocol); -int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp); -int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen); -int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen); -int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen); -int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen); -int win32_close(SOCKET fd); -ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags); -ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags); -typedef ULONG nfds_t; -int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout); - -#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION -#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res) -#undef gai_strerror -#define gai_strerror(errcode) win32_gai_strerror(errcode) -#define freeaddrinfo(res) win32_freeaddrinfo(res) -#define socket(domain, type, protocol) win32_socket(domain, type, protocol) -#define ioctl(fd, request, argp) win32_ioctl(fd, request, argp) -#define bind(sockfd, addr, addrlen) win32_bind(sockfd, addr, addrlen) -#define connect(sockfd, addr, addrlen) win32_connect(sockfd, addr, addrlen) -#define getsockopt(sockfd, level, optname, optval, optlen) win32_getsockopt(sockfd, level, optname, optval, optlen) -#define setsockopt(sockfd, level, optname, optval, optlen) win32_setsockopt(sockfd, level, optname, optval, optlen) -#define close(fd) win32_close(fd) -#define recv(sockfd, buf, len, flags) win32_recv(sockfd, buf, len, flags) -#define send(sockfd, buf, len, flags) win32_send(sockfd, buf, len, flags) -#define poll(fds, nfds, timeout) win32_poll(fds, nfds, timeout) -#endif /* REDIS_SOCKCOMPAT_IMPLEMENTATION */ -#endif /* _WIN32 */ - -#endif /* __SOCKCOMPAT_H */ diff --git a/ext/hiredis-1.0.2/ssl.c b/ext/hiredis-1.0.2/ssl.c deleted file mode 100644 index 7df58fbde..000000000 --- a/ext/hiredis-1.0.2/ssl.c +++ /dev/null @@ -1,526 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * Copyright (c) 2019, Redis Labs - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "hiredis.h" -#include "async.h" - -#include -#include -#include -#ifdef _WIN32 -#include -#else -#include -#endif - -#include -#include - -#include "win32.h" -#include "async_private.h" -#include "hiredis_ssl.h" - -void __redisSetError(redisContext *c, int type, const char *str); - -struct redisSSLContext { - /* Associated OpenSSL SSL_CTX as created by redisCreateSSLContext() */ - SSL_CTX *ssl_ctx; - - /* Requested SNI, or NULL */ - char *server_name; -}; - -/* The SSL connection context is attached to SSL/TLS connections as a privdata. */ -typedef struct redisSSL { - /** - * OpenSSL SSL object. - */ - SSL *ssl; - - /** - * SSL_write() requires to be called again with the same arguments it was - * previously called with in the event of an SSL_read/SSL_write situation - */ - size_t lastLen; - - /** Whether the SSL layer requires read (possibly before a write) */ - int wantRead; - - /** - * Whether a write was requested prior to a read. If set, the write() - * should resume whenever a read takes place, if possible - */ - int pendingWrite; -} redisSSL; - -/* Forward declaration */ -redisContextFuncs redisContextSSLFuncs; - -/** - * OpenSSL global initialization and locking handling callbacks. - * Note that this is only required for OpenSSL < 1.1.0. - */ - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#define HIREDIS_USE_CRYPTO_LOCKS -#endif - -#ifdef HIREDIS_USE_CRYPTO_LOCKS -#ifdef _WIN32 -typedef CRITICAL_SECTION sslLockType; -static void sslLockInit(sslLockType* l) { - InitializeCriticalSection(l); -} -static void sslLockAcquire(sslLockType* l) { - EnterCriticalSection(l); -} -static void sslLockRelease(sslLockType* l) { - LeaveCriticalSection(l); -} -#else -typedef pthread_mutex_t sslLockType; -static void sslLockInit(sslLockType *l) { - pthread_mutex_init(l, NULL); -} -static void sslLockAcquire(sslLockType *l) { - pthread_mutex_lock(l); -} -static void sslLockRelease(sslLockType *l) { - pthread_mutex_unlock(l); -} -#endif - -static sslLockType* ossl_locks; - -static void opensslDoLock(int mode, int lkid, const char *f, int line) { - sslLockType *l = ossl_locks + lkid; - - if (mode & CRYPTO_LOCK) { - sslLockAcquire(l); - } else { - sslLockRelease(l); - } - - (void)f; - (void)line; -} - -static int initOpensslLocks(void) { - unsigned ii, nlocks; - if (CRYPTO_get_locking_callback() != NULL) { - /* Someone already set the callback before us. Don't destroy it! */ - return REDIS_OK; - } - nlocks = CRYPTO_num_locks(); - ossl_locks = hi_malloc(sizeof(*ossl_locks) * nlocks); - if (ossl_locks == NULL) - return REDIS_ERR; - - for (ii = 0; ii < nlocks; ii++) { - sslLockInit(ossl_locks + ii); - } - CRYPTO_set_locking_callback(opensslDoLock); - return REDIS_OK; -} -#endif /* HIREDIS_USE_CRYPTO_LOCKS */ - -int redisInitOpenSSL(void) -{ - SSL_library_init(); -#ifdef HIREDIS_USE_CRYPTO_LOCKS - initOpensslLocks(); -#endif - - return REDIS_OK; -} - -/** - * redisSSLContext helper context destruction. - */ - -const char *redisSSLContextGetError(redisSSLContextError error) -{ - switch (error) { - case REDIS_SSL_CTX_NONE: - return "No Error"; - case REDIS_SSL_CTX_CREATE_FAILED: - return "Failed to create OpenSSL SSL_CTX"; - case REDIS_SSL_CTX_CERT_KEY_REQUIRED: - return "Client cert and key must both be specified or skipped"; - case REDIS_SSL_CTX_CA_CERT_LOAD_FAILED: - return "Failed to load CA Certificate or CA Path"; - case REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED: - return "Failed to load client certificate"; - case REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED: - return "Failed to load private key"; - default: - return "Unknown error code"; - } -} - -void redisFreeSSLContext(redisSSLContext *ctx) -{ - if (!ctx) - return; - - if (ctx->server_name) { - hi_free(ctx->server_name); - ctx->server_name = NULL; - } - - if (ctx->ssl_ctx) { - SSL_CTX_free(ctx->ssl_ctx); - ctx->ssl_ctx = NULL; - } - - hi_free(ctx); -} - - -/** - * redisSSLContext helper context initialization. - */ - -redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath, - const char *cert_filename, const char *private_key_filename, - const char *server_name, redisSSLContextError *error) -{ - redisSSLContext *ctx = hi_calloc(1, sizeof(redisSSLContext)); - if (ctx == NULL) - goto error; - - ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - if (!ctx->ssl_ctx) { - if (error) *error = REDIS_SSL_CTX_CREATE_FAILED; - goto error; - } - - SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); - SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL); - - if ((cert_filename != NULL && private_key_filename == NULL) || - (private_key_filename != NULL && cert_filename == NULL)) { - if (error) *error = REDIS_SSL_CTX_CERT_KEY_REQUIRED; - goto error; - } - - if (capath || cacert_filename) { - if (!SSL_CTX_load_verify_locations(ctx->ssl_ctx, cacert_filename, capath)) { - if (error) *error = REDIS_SSL_CTX_CA_CERT_LOAD_FAILED; - goto error; - } - } - - if (cert_filename) { - if (!SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, cert_filename)) { - if (error) *error = REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED; - goto error; - } - if (!SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, private_key_filename, SSL_FILETYPE_PEM)) { - if (error) *error = REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED; - goto error; - } - } - - if (server_name) - ctx->server_name = hi_strdup(server_name); - - return ctx; - -error: - redisFreeSSLContext(ctx); - return NULL; -} - -/** - * SSL Connection initialization. - */ - - -static int redisSSLConnect(redisContext *c, SSL *ssl) { - if (c->privctx) { - __redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated"); - return REDIS_ERR; - } - - redisSSL *rssl = hi_calloc(1, sizeof(redisSSL)); - if (rssl == NULL) { - __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); - return REDIS_ERR; - } - - c->funcs = &redisContextSSLFuncs; - rssl->ssl = ssl; - - SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - SSL_set_fd(rssl->ssl, c->fd); - SSL_set_connect_state(rssl->ssl); - - ERR_clear_error(); - int rv = SSL_connect(rssl->ssl); - if (rv == 1) { - c->privctx = rssl; - return REDIS_OK; - } - - rv = SSL_get_error(rssl->ssl, rv); - if (((c->flags & REDIS_BLOCK) == 0) && - (rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) { - c->privctx = rssl; - return REDIS_OK; - } - - if (c->err == 0) { - char err[512]; - if (rv == SSL_ERROR_SYSCALL) - snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",strerror(errno)); - else { - unsigned long e = ERR_peek_last_error(); - snprintf(err,sizeof(err)-1,"SSL_connect failed: %s", - ERR_reason_error_string(e)); - } - __redisSetError(c, REDIS_ERR_IO, err); - } - - hi_free(rssl); - return REDIS_ERR; -} - -/** - * A wrapper around redisSSLConnect() for users who manage their own context and - * create their own SSL object. - */ - -int redisInitiateSSL(redisContext *c, SSL *ssl) { - return redisSSLConnect(c, ssl); -} - -/** - * A wrapper around redisSSLConnect() for users who use redisSSLContext and don't - * manage their own SSL objects. - */ - -int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx) -{ - if (!c || !redis_ssl_ctx) - return REDIS_ERR; - - /* We want to verify that redisSSLConnect() won't fail on this, as it will - * not own the SSL object in that case and we'll end up leaking. - */ - if (c->privctx) - return REDIS_ERR; - - SSL *ssl = SSL_new(redis_ssl_ctx->ssl_ctx); - if (!ssl) { - __redisSetError(c, REDIS_ERR_OTHER, "Couldn't create new SSL instance"); - goto error; - } - - if (redis_ssl_ctx->server_name) { - if (!SSL_set_tlsext_host_name(ssl, redis_ssl_ctx->server_name)) { - __redisSetError(c, REDIS_ERR_OTHER, "Failed to set server_name/SNI"); - goto error; - } - } - - return redisSSLConnect(c, ssl); - -error: - if (ssl) - SSL_free(ssl); - return REDIS_ERR; -} - -static int maybeCheckWant(redisSSL *rssl, int rv) { - /** - * If the error is WANT_READ or WANT_WRITE, the appropriate flags are set - * and true is returned. False is returned otherwise - */ - if (rv == SSL_ERROR_WANT_READ) { - rssl->wantRead = 1; - return 1; - } else if (rv == SSL_ERROR_WANT_WRITE) { - rssl->pendingWrite = 1; - return 1; - } else { - return 0; - } -} - -/** - * Implementation of redisContextFuncs for SSL connections. - */ - -static void redisSSLFree(void *privctx){ - redisSSL *rsc = privctx; - - if (!rsc) return; - if (rsc->ssl) { - SSL_free(rsc->ssl); - rsc->ssl = NULL; - } - hi_free(rsc); -} - -static ssize_t redisSSLRead(redisContext *c, char *buf, size_t bufcap) { - redisSSL *rssl = c->privctx; - - int nread = SSL_read(rssl->ssl, buf, bufcap); - if (nread > 0) { - return nread; - } else if (nread == 0) { - __redisSetError(c, REDIS_ERR_EOF, "Server closed the connection"); - return -1; - } else { - int err = SSL_get_error(rssl->ssl, nread); - if (c->flags & REDIS_BLOCK) { - /** - * In blocking mode, we should never end up in a situation where - * we get an error without it being an actual error, except - * in the case of EINTR, which can be spuriously received from - * debuggers or whatever. - */ - if (errno == EINTR) { - return 0; - } else { - const char *msg = NULL; - if (errno == EAGAIN) { - msg = "Resource temporarily unavailable"; - } - __redisSetError(c, REDIS_ERR_IO, msg); - return -1; - } - } - - /** - * We can very well get an EWOULDBLOCK/EAGAIN, however - */ - if (maybeCheckWant(rssl, err)) { - return 0; - } else { - __redisSetError(c, REDIS_ERR_IO, NULL); - return -1; - } - } -} - -static ssize_t redisSSLWrite(redisContext *c) { - redisSSL *rssl = c->privctx; - - size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf); - int rv = SSL_write(rssl->ssl, c->obuf, len); - - if (rv > 0) { - rssl->lastLen = 0; - } else if (rv < 0) { - rssl->lastLen = len; - - int err = SSL_get_error(rssl->ssl, rv); - if ((c->flags & REDIS_BLOCK) == 0 && maybeCheckWant(rssl, err)) { - return 0; - } else { - __redisSetError(c, REDIS_ERR_IO, NULL); - return -1; - } - } - return rv; -} - -static void redisSSLAsyncRead(redisAsyncContext *ac) { - int rv; - redisSSL *rssl = ac->c.privctx; - redisContext *c = &ac->c; - - rssl->wantRead = 0; - - if (rssl->pendingWrite) { - int done; - - /* This is probably just a write event */ - rssl->pendingWrite = 0; - rv = redisBufferWrite(c, &done); - if (rv == REDIS_ERR) { - __redisAsyncDisconnect(ac); - return; - } else if (!done) { - _EL_ADD_WRITE(ac); - } - } - - rv = redisBufferRead(c); - if (rv == REDIS_ERR) { - __redisAsyncDisconnect(ac); - } else { - _EL_ADD_READ(ac); - redisProcessCallbacks(ac); - } -} - -static void redisSSLAsyncWrite(redisAsyncContext *ac) { - int rv, done = 0; - redisSSL *rssl = ac->c.privctx; - redisContext *c = &ac->c; - - rssl->pendingWrite = 0; - rv = redisBufferWrite(c, &done); - if (rv == REDIS_ERR) { - __redisAsyncDisconnect(ac); - return; - } - - if (!done) { - if (rssl->wantRead) { - /* Need to read-before-write */ - rssl->pendingWrite = 1; - _EL_DEL_WRITE(ac); - } else { - /* No extra reads needed, just need to write more */ - _EL_ADD_WRITE(ac); - } - } else { - /* Already done! */ - _EL_DEL_WRITE(ac); - } - - /* Always reschedule a read */ - _EL_ADD_READ(ac); -} - -redisContextFuncs redisContextSSLFuncs = { - .free_privctx = redisSSLFree, - .async_read = redisSSLAsyncRead, - .async_write = redisSSLAsyncWrite, - .read = redisSSLRead, - .write = redisSSLWrite -}; - diff --git a/ext/hiredis-1.0.2/test.c b/ext/hiredis-1.0.2/test.c deleted file mode 100644 index 397f5640e..000000000 --- a/ext/hiredis-1.0.2/test.c +++ /dev/null @@ -1,1401 +0,0 @@ -#include "fmacros.h" -#include "sockcompat.h" -#include -#include -#include -#ifndef _WIN32 -#include -#include -#endif -#include -#include -#include -#include - -#include "hiredis.h" -#include "async.h" -#ifdef HIREDIS_TEST_SSL -#include "hiredis_ssl.h" -#endif -#include "net.h" -#include "win32.h" - -enum connection_type { - CONN_TCP, - CONN_UNIX, - CONN_FD, - CONN_SSL -}; - -struct config { - enum connection_type type; - - struct { - const char *host; - int port; - struct timeval timeout; - } tcp; - - struct { - const char *path; - } unix_sock; - - struct { - const char *host; - int port; - const char *ca_cert; - const char *cert; - const char *key; - } ssl; -}; - -struct privdata { - int dtor_counter; -}; - -#ifdef HIREDIS_TEST_SSL -redisSSLContext *_ssl_ctx = NULL; -#endif - -/* The following lines make up our testing "framework" :) */ -static int tests = 0, fails = 0, skips = 0; -#define test(_s) { printf("#%02d ", ++tests); printf(_s); } -#define test_cond(_c) if(_c) printf("\033[0;32mPASSED\033[0;0m\n"); else {printf("\033[0;31mFAILED\033[0;0m\n"); fails++;} -#define test_skipped() { printf("\033[01;33mSKIPPED\033[0;0m\n"); skips++; } - -static long long usec(void) { -#ifndef _MSC_VER - struct timeval tv; - gettimeofday(&tv,NULL); - return (((long long)tv.tv_sec)*1000000)+tv.tv_usec; -#else - FILETIME ft; - GetSystemTimeAsFileTime(&ft); - return (((long long)ft.dwHighDateTime << 32) | ft.dwLowDateTime) / 10; -#endif -} - -/* The assert() calls below have side effects, so we need assert() - * even if we are compiling without asserts (-DNDEBUG). */ -#ifdef NDEBUG -#undef assert -#define assert(e) (void)(e) -#endif - -/* Helper to extract Redis version information. Aborts on any failure. */ -#define REDIS_VERSION_FIELD "redis_version:" -void get_redis_version(redisContext *c, int *majorptr, int *minorptr) { - redisReply *reply; - char *eptr, *s, *e; - int major, minor; - - reply = redisCommand(c, "INFO"); - if (reply == NULL || c->err || reply->type != REDIS_REPLY_STRING) - goto abort; - if ((s = strstr(reply->str, REDIS_VERSION_FIELD)) == NULL) - goto abort; - - s += strlen(REDIS_VERSION_FIELD); - - /* We need a field terminator and at least 'x.y.z' (5) bytes of data */ - if ((e = strstr(s, "\r\n")) == NULL || (e - s) < 5) - goto abort; - - /* Extract version info */ - major = strtol(s, &eptr, 10); - if (*eptr != '.') goto abort; - minor = strtol(eptr+1, NULL, 10); - - /* Push info the caller wants */ - if (majorptr) *majorptr = major; - if (minorptr) *minorptr = minor; - - freeReplyObject(reply); - return; - -abort: - freeReplyObject(reply); - fprintf(stderr, "Error: Cannot determine Redis version, aborting\n"); - exit(1); -} - -static redisContext *select_database(redisContext *c) { - redisReply *reply; - - /* Switch to DB 9 for testing, now that we know we can chat. */ - reply = redisCommand(c,"SELECT 9"); - assert(reply != NULL); - freeReplyObject(reply); - - /* Make sure the DB is emtpy */ - reply = redisCommand(c,"DBSIZE"); - assert(reply != NULL); - if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 0) { - /* Awesome, DB 9 is empty and we can continue. */ - freeReplyObject(reply); - } else { - printf("Database #9 is not empty, test can not continue\n"); - exit(1); - } - - return c; -} - -/* Switch protocol */ -static void send_hello(redisContext *c, int version) { - redisReply *reply; - int expected; - - reply = redisCommand(c, "HELLO %d", version); - expected = version == 3 ? REDIS_REPLY_MAP : REDIS_REPLY_ARRAY; - assert(reply != NULL && reply->type == expected); - freeReplyObject(reply); -} - -/* Togggle client tracking */ -static void send_client_tracking(redisContext *c, const char *str) { - redisReply *reply; - - reply = redisCommand(c, "CLIENT TRACKING %s", str); - assert(reply != NULL && reply->type == REDIS_REPLY_STATUS); - freeReplyObject(reply); -} - -static int disconnect(redisContext *c, int keep_fd) { - redisReply *reply; - - /* Make sure we're on DB 9. */ - reply = redisCommand(c,"SELECT 9"); - assert(reply != NULL); - freeReplyObject(reply); - reply = redisCommand(c,"FLUSHDB"); - assert(reply != NULL); - freeReplyObject(reply); - - /* Free the context as well, but keep the fd if requested. */ - if (keep_fd) - return redisFreeKeepFd(c); - redisFree(c); - return -1; -} - -static void do_ssl_handshake(redisContext *c) { -#ifdef HIREDIS_TEST_SSL - redisInitiateSSLWithContext(c, _ssl_ctx); - if (c->err) { - printf("SSL error: %s\n", c->errstr); - redisFree(c); - exit(1); - } -#else - (void) c; -#endif -} - -static redisContext *do_connect(struct config config) { - redisContext *c = NULL; - - if (config.type == CONN_TCP) { - c = redisConnect(config.tcp.host, config.tcp.port); - } else if (config.type == CONN_SSL) { - c = redisConnect(config.ssl.host, config.ssl.port); - } else if (config.type == CONN_UNIX) { - c = redisConnectUnix(config.unix_sock.path); - } else if (config.type == CONN_FD) { - /* Create a dummy connection just to get an fd to inherit */ - redisContext *dummy_ctx = redisConnectUnix(config.unix_sock.path); - if (dummy_ctx) { - int fd = disconnect(dummy_ctx, 1); - printf("Connecting to inherited fd %d\n", fd); - c = redisConnectFd(fd); - } - } else { - assert(NULL); - } - - if (c == NULL) { - printf("Connection error: can't allocate redis context\n"); - exit(1); - } else if (c->err) { - printf("Connection error: %s\n", c->errstr); - redisFree(c); - exit(1); - } - - if (config.type == CONN_SSL) { - do_ssl_handshake(c); - } - - return select_database(c); -} - -static void do_reconnect(redisContext *c, struct config config) { - redisReconnect(c); - - if (config.type == CONN_SSL) { - do_ssl_handshake(c); - } -} - -static void test_format_commands(void) { - char *cmd; - int len; - - test("Format command without interpolation: "); - len = redisFormatCommand(&cmd,"SET foo bar"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - hi_free(cmd); - - test("Format command with %%s string interpolation: "); - len = redisFormatCommand(&cmd,"SET %s %s","foo","bar"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - hi_free(cmd); - - test("Format command with %%s and an empty string: "); - len = redisFormatCommand(&cmd,"SET %s %s","foo",""); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(0+2)); - hi_free(cmd); - - test("Format command with an empty string in between proper interpolations: "); - len = redisFormatCommand(&cmd,"SET %s %s","","foo"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len) == 0 && - len == 4+4+(3+2)+4+(0+2)+4+(3+2)); - hi_free(cmd); - - test("Format command with %%b string interpolation: "); - len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"b\0r",(size_t)3); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - hi_free(cmd); - - test("Format command with %%b and an empty string: "); - len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"",(size_t)0); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(0+2)); - hi_free(cmd); - - test("Format command with literal %%: "); - len = redisFormatCommand(&cmd,"SET %% %%"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 && - len == 4+4+(3+2)+4+(1+2)+4+(1+2)); - hi_free(cmd); - - /* Vararg width depends on the type. These tests make sure that the - * width is correctly determined using the format and subsequent varargs - * can correctly be interpolated. */ -#define INTEGER_WIDTH_TEST(fmt, type) do { \ - type value = 123; \ - test("Format command with printf-delegation (" #type "): "); \ - len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \ - test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \ - len == 4+5+(12+2)+4+(9+2)); \ - hi_free(cmd); \ -} while(0) - -#define FLOAT_WIDTH_TEST(type) do { \ - type value = 123.0; \ - test("Format command with printf-delegation (" #type "): "); \ - len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \ - test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \ - len == 4+5+(12+2)+4+(9+2)); \ - hi_free(cmd); \ -} while(0) - - INTEGER_WIDTH_TEST("d", int); - INTEGER_WIDTH_TEST("hhd", char); - INTEGER_WIDTH_TEST("hd", short); - INTEGER_WIDTH_TEST("ld", long); - INTEGER_WIDTH_TEST("lld", long long); - INTEGER_WIDTH_TEST("u", unsigned int); - INTEGER_WIDTH_TEST("hhu", unsigned char); - INTEGER_WIDTH_TEST("hu", unsigned short); - INTEGER_WIDTH_TEST("lu", unsigned long); - INTEGER_WIDTH_TEST("llu", unsigned long long); - FLOAT_WIDTH_TEST(float); - FLOAT_WIDTH_TEST(double); - - test("Format command with invalid printf format: "); - len = redisFormatCommand(&cmd,"key:%08p %b",(void*)1234,"foo",(size_t)3); - test_cond(len == -1); - - const char *argv[3]; - argv[0] = "SET"; - argv[1] = "foo\0xxx"; - argv[2] = "bar"; - size_t lens[3] = { 3, 7, 3 }; - int argc = 3; - - test("Format command by passing argc/argv without lengths: "); - len = redisFormatCommandArgv(&cmd,argc,argv,NULL); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - hi_free(cmd); - - test("Format command by passing argc/argv with lengths: "); - len = redisFormatCommandArgv(&cmd,argc,argv,lens); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(7+2)+4+(3+2)); - hi_free(cmd); - - sds sds_cmd; - - sds_cmd = NULL; - test("Format command into sds by passing argc/argv without lengths: "); - len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,NULL); - test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - sdsfree(sds_cmd); - - sds_cmd = NULL; - test("Format command into sds by passing argc/argv with lengths: "); - len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,lens); - test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(7+2)+4+(3+2)); - sdsfree(sds_cmd); -} - -static void test_append_formatted_commands(struct config config) { - redisContext *c; - redisReply *reply; - char *cmd; - int len; - - c = do_connect(config); - - test("Append format command: "); - - len = redisFormatCommand(&cmd, "SET foo bar"); - - test_cond(redisAppendFormattedCommand(c, cmd, len) == REDIS_OK); - - assert(redisGetReply(c, (void*)&reply) == REDIS_OK); - - hi_free(cmd); - freeReplyObject(reply); - - disconnect(c, 0); -} - -static void test_reply_reader(void) { - redisReader *reader; - void *reply, *root; - int ret; - int i; - - test("Error handling in reply parser: "); - reader = redisReaderCreate(); - redisReaderFeed(reader,(char*)"@foo\r\n",6); - ret = redisReaderGetReply(reader,NULL); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0); - redisReaderFree(reader); - - /* when the reply already contains multiple items, they must be free'd - * on an error. valgrind will bark when this doesn't happen. */ - test("Memory cleanup in reply parser: "); - reader = redisReaderCreate(); - redisReaderFeed(reader,(char*)"*2\r\n",4); - redisReaderFeed(reader,(char*)"$5\r\nhello\r\n",11); - redisReaderFeed(reader,(char*)"@foo\r\n",6); - ret = redisReaderGetReply(reader,NULL); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0); - redisReaderFree(reader); - - reader = redisReaderCreate(); - test("Can handle arbitrarily nested multi-bulks: "); - for (i = 0; i < 128; i++) { - redisReaderFeed(reader,(char*)"*1\r\n", 4); - } - redisReaderFeed(reader,(char*)"$6\r\nLOLWUT\r\n",12); - ret = redisReaderGetReply(reader,&reply); - root = reply; /* Keep track of the root reply */ - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_ARRAY && - ((redisReply*)reply)->elements == 1); - - test("Can parse arbitrarily nested multi-bulks correctly: "); - while(i--) { - assert(reply != NULL && ((redisReply*)reply)->type == REDIS_REPLY_ARRAY); - reply = ((redisReply*)reply)->element[0]; - } - test_cond(((redisReply*)reply)->type == REDIS_REPLY_STRING && - !memcmp(((redisReply*)reply)->str, "LOLWUT", 6)); - freeReplyObject(root); - redisReaderFree(reader); - - test("Correctly parses LLONG_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":9223372036854775807\r\n",22); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_INTEGER && - ((redisReply*)reply)->integer == LLONG_MAX); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when > LLONG_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":9223372036854775808\r\n",22); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bad integer value") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Correctly parses LLONG_MIN: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":-9223372036854775808\r\n",23); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_INTEGER && - ((redisReply*)reply)->integer == LLONG_MIN); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when < LLONG_MIN: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":-9223372036854775809\r\n",23); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bad integer value") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when array < -1: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "*-2\r\n+asdf\r\n",12); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when bulk < -1: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "$-2\r\nasdf\r\n",11); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bulk string length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Can configure maximum multi-bulk elements: "); - reader = redisReaderCreate(); - reader->maxelements = 1024; - redisReaderFeed(reader, "*1025\r\n", 7); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr, "Multi-bulk length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Multi-bulk never overflows regardless of maxelements: "); - size_t bad_mbulk_len = (SIZE_MAX / sizeof(void *)) + 3; - char bad_mbulk_reply[100]; - snprintf(bad_mbulk_reply, sizeof(bad_mbulk_reply), "*%llu\r\n+asdf\r\n", - (unsigned long long) bad_mbulk_len); - - reader = redisReaderCreate(); - reader->maxelements = 0; /* Don't rely on default limit */ - redisReaderFeed(reader, bad_mbulk_reply, strlen(bad_mbulk_reply)); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && strcasecmp(reader->errstr, "Out of memory") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - -#if LLONG_MAX > SIZE_MAX - test("Set error when array > SIZE_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "*9223372036854775807\r\n+asdf\r\n",29); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when bulk > SIZE_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "$9223372036854775807\r\nasdf\r\n",28); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bulk string length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); -#endif - - test("Works with NULL functions for reply: "); - reader = redisReaderCreate(); - reader->fn = NULL; - redisReaderFeed(reader,(char*)"+OK\r\n",5); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS); - redisReaderFree(reader); - - test("Works when a single newline (\\r\\n) covers two calls to feed: "); - reader = redisReaderCreate(); - reader->fn = NULL; - redisReaderFeed(reader,(char*)"+OK\r",4); - ret = redisReaderGetReply(reader,&reply); - assert(ret == REDIS_OK && reply == NULL); - redisReaderFeed(reader,(char*)"\n",1); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS); - redisReaderFree(reader); - - test("Don't reset state after protocol error: "); - reader = redisReaderCreate(); - reader->fn = NULL; - redisReaderFeed(reader,(char*)"x",1); - ret = redisReaderGetReply(reader,&reply); - assert(ret == REDIS_ERR); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && reply == NULL); - redisReaderFree(reader); - - /* Regression test for issue #45 on GitHub. */ - test("Don't do empty allocation for empty multi bulk: "); - reader = redisReaderCreate(); - redisReaderFeed(reader,(char*)"*0\r\n",4); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_ARRAY && - ((redisReply*)reply)->elements == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - /* RESP3 verbatim strings (GitHub issue #802) */ - test("Can parse RESP3 verbatim strings: "); - reader = redisReaderCreate(); - redisReaderFeed(reader,(char*)"=10\r\ntxt:LOLWUT\r\n",17); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_VERB && - !memcmp(((redisReply*)reply)->str,"LOLWUT", 6)); - freeReplyObject(reply); - redisReaderFree(reader); - - /* RESP3 push messages (Github issue #815) */ - test("Can parse RESP3 push messages: "); - reader = redisReaderCreate(); - redisReaderFeed(reader,(char*)">2\r\n$6\r\nLOLWUT\r\n:42\r\n",21); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_PUSH && - ((redisReply*)reply)->elements == 2 && - ((redisReply*)reply)->element[0]->type == REDIS_REPLY_STRING && - !memcmp(((redisReply*)reply)->element[0]->str,"LOLWUT",6) && - ((redisReply*)reply)->element[1]->type == REDIS_REPLY_INTEGER && - ((redisReply*)reply)->element[1]->integer == 42); - freeReplyObject(reply); - redisReaderFree(reader); -} - -static void test_free_null(void) { - void *redisCtx = NULL; - void *reply = NULL; - - test("Don't fail when redisFree is passed a NULL value: "); - redisFree(redisCtx); - test_cond(redisCtx == NULL); - - test("Don't fail when freeReplyObject is passed a NULL value: "); - freeReplyObject(reply); - test_cond(reply == NULL); -} - -static void *hi_malloc_fail(size_t size) { - (void)size; - return NULL; -} - -static void *hi_calloc_fail(size_t nmemb, size_t size) { - (void)nmemb; - (void)size; - return NULL; -} - -static void *hi_realloc_fail(void *ptr, size_t size) { - (void)ptr; - (void)size; - return NULL; -} - -static void test_allocator_injection(void) { - hiredisAllocFuncs ha = { - .mallocFn = hi_malloc_fail, - .callocFn = hi_calloc_fail, - .reallocFn = hi_realloc_fail, - .strdupFn = strdup, - .freeFn = free, - }; - - // Override hiredis allocators - hiredisSetAllocators(&ha); - - test("redisContext uses injected allocators: "); - redisContext *c = redisConnect("localhost", 6379); - test_cond(c == NULL); - - test("redisReader uses injected allocators: "); - redisReader *reader = redisReaderCreate(); - test_cond(reader == NULL); - - // Return allocators to default - hiredisResetAllocators(); -} - -#define HIREDIS_BAD_DOMAIN "idontexist-noreally.com" -static void test_blocking_connection_errors(void) { - redisContext *c; - struct addrinfo hints = {.ai_family = AF_INET}; - struct addrinfo *ai_tmp = NULL; - - int rv = getaddrinfo(HIREDIS_BAD_DOMAIN, "6379", &hints, &ai_tmp); - if (rv != 0) { - // Address does *not* exist - test("Returns error when host cannot be resolved: "); - // First see if this domain name *actually* resolves to NXDOMAIN - c = redisConnect(HIREDIS_BAD_DOMAIN, 6379); - test_cond( - c->err == REDIS_ERR_OTHER && - (strcmp(c->errstr, "Name or service not known") == 0 || - strcmp(c->errstr, "Can't resolve: " HIREDIS_BAD_DOMAIN) == 0 || - strcmp(c->errstr, "Name does not resolve") == 0 || - strcmp(c->errstr, "nodename nor servname provided, or not known") == 0 || - strcmp(c->errstr, "No address associated with hostname") == 0 || - strcmp(c->errstr, "Temporary failure in name resolution") == 0 || - strcmp(c->errstr, "hostname nor servname provided, or not known") == 0 || - strcmp(c->errstr, "no address associated with name") == 0 || - strcmp(c->errstr, "No such host is known. ") == 0)); - redisFree(c); - } else { - printf("Skipping NXDOMAIN test. Found evil ISP!\n"); - freeaddrinfo(ai_tmp); - } - -#ifndef _WIN32 - test("Returns error when the port is not open: "); - c = redisConnect((char*)"localhost", 1); - test_cond(c->err == REDIS_ERR_IO && - strcmp(c->errstr,"Connection refused") == 0); - redisFree(c); - - test("Returns error when the unix_sock socket path doesn't accept connections: "); - c = redisConnectUnix((char*)"/tmp/idontexist.sock"); - test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */ - redisFree(c); -#endif -} - -/* Dummy push handler */ -void push_handler(void *privdata, void *reply) { - int *counter = privdata; - freeReplyObject(reply); - *counter += 1; -} - -/* Dummy function just to test setting a callback with redisOptions */ -void push_handler_async(redisAsyncContext *ac, void *reply) { - (void)ac; - (void)reply; -} - -static void test_resp3_push_handler(redisContext *c) { - redisPushFn *old = NULL; - redisReply *reply; - void *privdata; - int n = 0; - - /* Switch to RESP3 and turn on client tracking */ - send_hello(c, 3); - send_client_tracking(c, "ON"); - privdata = c->privdata; - c->privdata = &n; - - reply = redisCommand(c, "GET key:0"); - assert(reply != NULL); - freeReplyObject(reply); - - test("RESP3 PUSH messages are handled out of band by default: "); - reply = redisCommand(c, "SET key:0 val:0"); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS); - freeReplyObject(reply); - - assert((reply = redisCommand(c, "GET key:0")) != NULL); - freeReplyObject(reply); - - old = redisSetPushCallback(c, push_handler); - test("We can set a custom RESP3 PUSH handler: "); - reply = redisCommand(c, "SET key:0 val:0"); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && n == 1); - freeReplyObject(reply); - - /* Unset the push callback and generate an invalidate message making - * sure it is not handled out of band. */ - test("With no handler, PUSH replies come in-band: "); - redisSetPushCallback(c, NULL); - assert((reply = redisCommand(c, "GET key:0")) != NULL); - freeReplyObject(reply); - assert((reply = redisCommand(c, "SET key:0 invalid")) != NULL); - test_cond(reply->type == REDIS_REPLY_PUSH); - freeReplyObject(reply); - - test("With no PUSH handler, no replies are lost: "); - assert(redisGetReply(c, (void**)&reply) == REDIS_OK); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS); - freeReplyObject(reply); - - /* Return to the originally set PUSH handler */ - assert(old != NULL); - redisSetPushCallback(c, old); - - /* Switch back to RESP2 and disable tracking */ - c->privdata = privdata; - send_client_tracking(c, "OFF"); - send_hello(c, 2); -} - -redisOptions get_redis_tcp_options(struct config config) { - redisOptions options = {0}; - REDIS_OPTIONS_SET_TCP(&options, config.tcp.host, config.tcp.port); - return options; -} - -static void test_resp3_push_options(struct config config) { - redisAsyncContext *ac; - redisContext *c; - redisOptions options; - - test("We set a default RESP3 handler for redisContext: "); - options = get_redis_tcp_options(config); - assert((c = redisConnectWithOptions(&options)) != NULL); - test_cond(c->push_cb != NULL); - redisFree(c); - - test("We don't set a default RESP3 push handler for redisAsyncContext: "); - options = get_redis_tcp_options(config); - assert((ac = redisAsyncConnectWithOptions(&options)) != NULL); - test_cond(ac->c.push_cb == NULL); - redisAsyncFree(ac); - - test("Our REDIS_OPT_NO_PUSH_AUTOFREE flag works: "); - options = get_redis_tcp_options(config); - options.options |= REDIS_OPT_NO_PUSH_AUTOFREE; - assert((c = redisConnectWithOptions(&options)) != NULL); - test_cond(c->push_cb == NULL); - redisFree(c); - - test("We can use redisOptions to set a custom PUSH handler for redisContext: "); - options = get_redis_tcp_options(config); - options.push_cb = push_handler; - assert((c = redisConnectWithOptions(&options)) != NULL); - test_cond(c->push_cb == push_handler); - redisFree(c); - - test("We can use redisOptions to set a custom PUSH handler for redisAsyncContext: "); - options = get_redis_tcp_options(config); - options.async_push_cb = push_handler_async; - assert((ac = redisAsyncConnectWithOptions(&options)) != NULL); - test_cond(ac->push_cb == push_handler_async); - redisAsyncFree(ac); -} - -void free_privdata(void *privdata) { - struct privdata *data = privdata; - data->dtor_counter++; -} - -static void test_privdata_hooks(struct config config) { - struct privdata data = {0}; - redisOptions options; - redisContext *c; - - test("We can use redisOptions to set privdata: "); - options = get_redis_tcp_options(config); - REDIS_OPTIONS_SET_PRIVDATA(&options, &data, free_privdata); - assert((c = redisConnectWithOptions(&options)) != NULL); - test_cond(c->privdata == &data); - - test("Our privdata destructor fires when we free the context: "); - redisFree(c); - test_cond(data.dtor_counter == 1); -} - -static void test_blocking_connection(struct config config) { - redisContext *c; - redisReply *reply; - int major; - - c = do_connect(config); - - test("Is able to deliver commands: "); - reply = redisCommand(c,"PING"); - test_cond(reply->type == REDIS_REPLY_STATUS && - strcasecmp(reply->str,"pong") == 0) - freeReplyObject(reply); - - test("Is a able to send commands verbatim: "); - reply = redisCommand(c,"SET foo bar"); - test_cond (reply->type == REDIS_REPLY_STATUS && - strcasecmp(reply->str,"ok") == 0) - freeReplyObject(reply); - - test("%%s String interpolation works: "); - reply = redisCommand(c,"SET %s %s","foo","hello world"); - freeReplyObject(reply); - reply = redisCommand(c,"GET foo"); - test_cond(reply->type == REDIS_REPLY_STRING && - strcmp(reply->str,"hello world") == 0); - freeReplyObject(reply); - - test("%%b String interpolation works: "); - reply = redisCommand(c,"SET %b %b","foo",(size_t)3,"hello\x00world",(size_t)11); - freeReplyObject(reply); - reply = redisCommand(c,"GET foo"); - test_cond(reply->type == REDIS_REPLY_STRING && - memcmp(reply->str,"hello\x00world",11) == 0) - - test("Binary reply length is correct: "); - test_cond(reply->len == 11) - freeReplyObject(reply); - - test("Can parse nil replies: "); - reply = redisCommand(c,"GET nokey"); - test_cond(reply->type == REDIS_REPLY_NIL) - freeReplyObject(reply); - - /* test 7 */ - test("Can parse integer replies: "); - reply = redisCommand(c,"INCR mycounter"); - test_cond(reply->type == REDIS_REPLY_INTEGER && reply->integer == 1) - freeReplyObject(reply); - - test("Can parse multi bulk replies: "); - freeReplyObject(redisCommand(c,"LPUSH mylist foo")); - freeReplyObject(redisCommand(c,"LPUSH mylist bar")); - reply = redisCommand(c,"LRANGE mylist 0 -1"); - test_cond(reply->type == REDIS_REPLY_ARRAY && - reply->elements == 2 && - !memcmp(reply->element[0]->str,"bar",3) && - !memcmp(reply->element[1]->str,"foo",3)) - freeReplyObject(reply); - - /* m/e with multi bulk reply *before* other reply. - * specifically test ordering of reply items to parse. */ - test("Can handle nested multi bulk replies: "); - freeReplyObject(redisCommand(c,"MULTI")); - freeReplyObject(redisCommand(c,"LRANGE mylist 0 -1")); - freeReplyObject(redisCommand(c,"PING")); - reply = (redisCommand(c,"EXEC")); - test_cond(reply->type == REDIS_REPLY_ARRAY && - reply->elements == 2 && - reply->element[0]->type == REDIS_REPLY_ARRAY && - reply->element[0]->elements == 2 && - !memcmp(reply->element[0]->element[0]->str,"bar",3) && - !memcmp(reply->element[0]->element[1]->str,"foo",3) && - reply->element[1]->type == REDIS_REPLY_STATUS && - strcasecmp(reply->element[1]->str,"pong") == 0); - freeReplyObject(reply); - - /* Make sure passing NULL to redisGetReply is safe */ - test("Can pass NULL to redisGetReply: "); - assert(redisAppendCommand(c, "PING") == REDIS_OK); - test_cond(redisGetReply(c, NULL) == REDIS_OK); - - get_redis_version(c, &major, NULL); - if (major >= 6) test_resp3_push_handler(c); - test_resp3_push_options(config); - - test_privdata_hooks(config); - - disconnect(c, 0); -} - -/* Send DEBUG SLEEP 0 to detect if we have this command */ -static int detect_debug_sleep(redisContext *c) { - int detected; - redisReply *reply = redisCommand(c, "DEBUG SLEEP 0\r\n"); - - if (reply == NULL || c->err) { - const char *cause = c->err ? c->errstr : "(none)"; - fprintf(stderr, "Error testing for DEBUG SLEEP (Redis error: %s), exiting\n", cause); - exit(-1); - } - - detected = reply->type == REDIS_REPLY_STATUS; - freeReplyObject(reply); - - return detected; -} - -static void test_blocking_connection_timeouts(struct config config) { - redisContext *c; - redisReply *reply; - ssize_t s; - const char *sleep_cmd = "DEBUG SLEEP 3\r\n"; - struct timeval tv; - - c = do_connect(config); - test("Successfully completes a command when the timeout is not exceeded: "); - reply = redisCommand(c,"SET foo fast"); - freeReplyObject(reply); - tv.tv_sec = 0; - tv.tv_usec = 10000; - redisSetTimeout(c, tv); - reply = redisCommand(c, "GET foo"); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STRING && memcmp(reply->str, "fast", 4) == 0); - freeReplyObject(reply); - disconnect(c, 0); - - c = do_connect(config); - test("Does not return a reply when the command times out: "); - if (detect_debug_sleep(c)) { - redisAppendFormattedCommand(c, sleep_cmd, strlen(sleep_cmd)); - s = c->funcs->write(c); - tv.tv_sec = 0; - tv.tv_usec = 10000; - redisSetTimeout(c, tv); - reply = redisCommand(c, "GET foo"); -#ifndef _WIN32 - test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO && - strcmp(c->errstr, "Resource temporarily unavailable") == 0); -#else - test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_TIMEOUT && - strcmp(c->errstr, "recv timeout") == 0); -#endif - freeReplyObject(reply); - } else { - test_skipped(); - } - - test("Reconnect properly reconnects after a timeout: "); - do_reconnect(c, config); - reply = redisCommand(c, "PING"); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0); - freeReplyObject(reply); - - test("Reconnect properly uses owned parameters: "); - config.tcp.host = "foo"; - config.unix_sock.path = "foo"; - do_reconnect(c, config); - reply = redisCommand(c, "PING"); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0); - freeReplyObject(reply); - - disconnect(c, 0); -} - -static void test_blocking_io_errors(struct config config) { - redisContext *c; - redisReply *reply; - void *_reply; - int major, minor; - - /* Connect to target given by config. */ - c = do_connect(config); - get_redis_version(c, &major, &minor); - - test("Returns I/O error when the connection is lost: "); - reply = redisCommand(c,"QUIT"); - if (major > 2 || (major == 2 && minor > 0)) { - /* > 2.0 returns OK on QUIT and read() should be issued once more - * to know the descriptor is at EOF. */ - test_cond(strcasecmp(reply->str,"OK") == 0 && - redisGetReply(c,&_reply) == REDIS_ERR); - freeReplyObject(reply); - } else { - test_cond(reply == NULL); - } - -#ifndef _WIN32 - /* On 2.0, QUIT will cause the connection to be closed immediately and - * the read(2) for the reply on QUIT will set the error to EOF. - * On >2.0, QUIT will return with OK and another read(2) needed to be - * issued to find out the socket was closed by the server. In both - * conditions, the error will be set to EOF. */ - assert(c->err == REDIS_ERR_EOF && - strcmp(c->errstr,"Server closed the connection") == 0); -#endif - redisFree(c); - - c = do_connect(config); - test("Returns I/O error on socket timeout: "); - struct timeval tv = { 0, 1000 }; - assert(redisSetTimeout(c,tv) == REDIS_OK); - int respcode = redisGetReply(c,&_reply); -#ifndef _WIN32 - test_cond(respcode == REDIS_ERR && c->err == REDIS_ERR_IO && errno == EAGAIN); -#else - test_cond(respcode == REDIS_ERR && c->err == REDIS_ERR_TIMEOUT); -#endif - redisFree(c); -} - -static void test_invalid_timeout_errors(struct config config) { - redisContext *c; - - test("Set error when an invalid timeout usec value is given to redisConnectWithTimeout: "); - - config.tcp.timeout.tv_sec = 0; - config.tcp.timeout.tv_usec = 10000001; - - c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout); - - test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); - redisFree(c); - - test("Set error when an invalid timeout sec value is given to redisConnectWithTimeout: "); - - config.tcp.timeout.tv_sec = (((LONG_MAX) - 999) / 1000) + 1; - config.tcp.timeout.tv_usec = 0; - - c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout); - - test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); - redisFree(c); -} - -/* Wrap malloc to abort on failure so OOM checks don't make the test logic - * harder to follow. */ -void *hi_malloc_safe(size_t size) { - void *ptr = hi_malloc(size); - if (ptr == NULL) { - fprintf(stderr, "Error: Out of memory\n"); - exit(-1); - } - - return ptr; -} - -static void test_throughput(struct config config) { - redisContext *c = do_connect(config); - redisReply **replies; - int i, num; - long long t1, t2; - - test("Throughput:\n"); - for (i = 0; i < 500; i++) - freeReplyObject(redisCommand(c,"LPUSH mylist foo")); - - num = 1000; - replies = hi_malloc_safe(sizeof(redisReply*)*num); - t1 = usec(); - for (i = 0; i < num; i++) { - replies[i] = redisCommand(c,"PING"); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - hi_free(replies); - printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = hi_malloc_safe(sizeof(redisReply*)*num); - t1 = usec(); - for (i = 0; i < num; i++) { - replies[i] = redisCommand(c,"LRANGE mylist 0 499"); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY); - assert(replies[i] != NULL && replies[i]->elements == 500); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - hi_free(replies); - printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = hi_malloc_safe(sizeof(redisReply*)*num); - t1 = usec(); - for (i = 0; i < num; i++) { - replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - hi_free(replies); - printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0); - - num = 10000; - replies = hi_malloc_safe(sizeof(redisReply*)*num); - for (i = 0; i < num; i++) - redisAppendCommand(c,"PING"); - t1 = usec(); - for (i = 0; i < num; i++) { - assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - hi_free(replies); - printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = hi_malloc_safe(sizeof(redisReply*)*num); - for (i = 0; i < num; i++) - redisAppendCommand(c,"LRANGE mylist 0 499"); - t1 = usec(); - for (i = 0; i < num; i++) { - assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY); - assert(replies[i] != NULL && replies[i]->elements == 500); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - hi_free(replies); - printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = hi_malloc_safe(sizeof(redisReply*)*num); - for (i = 0; i < num; i++) - redisAppendCommand(c,"INCRBY incrkey %d", 1000000); - t1 = usec(); - for (i = 0; i < num; i++) { - assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - hi_free(replies); - printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); - - disconnect(c, 0); -} - -// static long __test_callback_flags = 0; -// static void __test_callback(redisContext *c, void *privdata) { -// ((void)c); -// /* Shift to detect execution order */ -// __test_callback_flags <<= 8; -// __test_callback_flags |= (long)privdata; -// } -// -// static void __test_reply_callback(redisContext *c, redisReply *reply, void *privdata) { -// ((void)c); -// /* Shift to detect execution order */ -// __test_callback_flags <<= 8; -// __test_callback_flags |= (long)privdata; -// if (reply) freeReplyObject(reply); -// } -// -// static redisContext *__connect_nonblock() { -// /* Reset callback flags */ -// __test_callback_flags = 0; -// return redisConnectNonBlock("127.0.0.1", port, NULL); -// } -// -// static void test_nonblocking_connection() { -// redisContext *c; -// int wdone = 0; -// -// test("Calls command callback when command is issued: "); -// c = __connect_nonblock(); -// redisSetCommandCallback(c,__test_callback,(void*)1); -// redisCommand(c,"PING"); -// test_cond(__test_callback_flags == 1); -// redisFree(c); -// -// test("Calls disconnect callback on redisDisconnect: "); -// c = __connect_nonblock(); -// redisSetDisconnectCallback(c,__test_callback,(void*)2); -// redisDisconnect(c); -// test_cond(__test_callback_flags == 2); -// redisFree(c); -// -// test("Calls disconnect callback and free callback on redisFree: "); -// c = __connect_nonblock(); -// redisSetDisconnectCallback(c,__test_callback,(void*)2); -// redisSetFreeCallback(c,__test_callback,(void*)4); -// redisFree(c); -// test_cond(__test_callback_flags == ((2 << 8) | 4)); -// -// test("redisBufferWrite against empty write buffer: "); -// c = __connect_nonblock(); -// test_cond(redisBufferWrite(c,&wdone) == REDIS_OK && wdone == 1); -// redisFree(c); -// -// test("redisBufferWrite against not yet connected fd: "); -// c = __connect_nonblock(); -// redisCommand(c,"PING"); -// test_cond(redisBufferWrite(c,NULL) == REDIS_ERR && -// strncmp(c->error,"write:",6) == 0); -// redisFree(c); -// -// test("redisBufferWrite against closed fd: "); -// c = __connect_nonblock(); -// redisCommand(c,"PING"); -// redisDisconnect(c); -// test_cond(redisBufferWrite(c,NULL) == REDIS_ERR && -// strncmp(c->error,"write:",6) == 0); -// redisFree(c); -// -// test("Process callbacks in the right sequence: "); -// c = __connect_nonblock(); -// redisCommandWithCallback(c,__test_reply_callback,(void*)1,"PING"); -// redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING"); -// redisCommandWithCallback(c,__test_reply_callback,(void*)3,"PING"); -// -// /* Write output buffer */ -// wdone = 0; -// while(!wdone) { -// usleep(500); -// redisBufferWrite(c,&wdone); -// } -// -// /* Read until at least one callback is executed (the 3 replies will -// * arrive in a single packet, causing all callbacks to be executed in -// * a single pass). */ -// while(__test_callback_flags == 0) { -// assert(redisBufferRead(c) == REDIS_OK); -// redisProcessCallbacks(c); -// } -// test_cond(__test_callback_flags == 0x010203); -// redisFree(c); -// -// test("redisDisconnect executes pending callbacks with NULL reply: "); -// c = __connect_nonblock(); -// redisSetDisconnectCallback(c,__test_callback,(void*)1); -// redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING"); -// redisDisconnect(c); -// test_cond(__test_callback_flags == 0x0201); -// redisFree(c); -// } - -int main(int argc, char **argv) { - struct config cfg = { - .tcp = { - .host = "127.0.0.1", - .port = 6379 - }, - .unix_sock = { - .path = "/tmp/redis.sock" - } - }; - int throughput = 1; - int test_inherit_fd = 1; - int skips_as_fails = 0; - int test_unix_socket; - - /* Parse command line options. */ - argv++; argc--; - while (argc) { - if (argc >= 2 && !strcmp(argv[0],"-h")) { - argv++; argc--; - cfg.tcp.host = argv[0]; - } else if (argc >= 2 && !strcmp(argv[0],"-p")) { - argv++; argc--; - cfg.tcp.port = atoi(argv[0]); - } else if (argc >= 2 && !strcmp(argv[0],"-s")) { - argv++; argc--; - cfg.unix_sock.path = argv[0]; - } else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) { - throughput = 0; - } else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) { - test_inherit_fd = 0; - } else if (argc >= 1 && !strcmp(argv[0],"--skips-as-fails")) { - skips_as_fails = 1; -#ifdef HIREDIS_TEST_SSL - } else if (argc >= 2 && !strcmp(argv[0],"--ssl-port")) { - argv++; argc--; - cfg.ssl.port = atoi(argv[0]); - } else if (argc >= 2 && !strcmp(argv[0],"--ssl-host")) { - argv++; argc--; - cfg.ssl.host = argv[0]; - } else if (argc >= 2 && !strcmp(argv[0],"--ssl-ca-cert")) { - argv++; argc--; - cfg.ssl.ca_cert = argv[0]; - } else if (argc >= 2 && !strcmp(argv[0],"--ssl-cert")) { - argv++; argc--; - cfg.ssl.cert = argv[0]; - } else if (argc >= 2 && !strcmp(argv[0],"--ssl-key")) { - argv++; argc--; - cfg.ssl.key = argv[0]; -#endif - } else { - fprintf(stderr, "Invalid argument: %s\n", argv[0]); - exit(1); - } - argv++; argc--; - } - -#ifndef _WIN32 - /* Ignore broken pipe signal (for I/O error tests). */ - signal(SIGPIPE, SIG_IGN); - - test_unix_socket = access(cfg.unix_sock.path, F_OK) == 0; - -#else - /* Unix sockets don't exist in Windows */ - test_unix_socket = 0; -#endif - - test_allocator_injection(); - - test_format_commands(); - test_reply_reader(); - test_blocking_connection_errors(); - test_free_null(); - - printf("\nTesting against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port); - cfg.type = CONN_TCP; - test_blocking_connection(cfg); - test_blocking_connection_timeouts(cfg); - test_blocking_io_errors(cfg); - test_invalid_timeout_errors(cfg); - test_append_formatted_commands(cfg); - if (throughput) test_throughput(cfg); - - printf("\nTesting against Unix socket connection (%s): ", cfg.unix_sock.path); - if (test_unix_socket) { - printf("\n"); - cfg.type = CONN_UNIX; - test_blocking_connection(cfg); - test_blocking_connection_timeouts(cfg); - test_blocking_io_errors(cfg); - if (throughput) test_throughput(cfg); - } else { - test_skipped(); - } - -#ifdef HIREDIS_TEST_SSL - if (cfg.ssl.port && cfg.ssl.host) { - - redisInitOpenSSL(); - _ssl_ctx = redisCreateSSLContext(cfg.ssl.ca_cert, NULL, cfg.ssl.cert, cfg.ssl.key, NULL, NULL); - assert(_ssl_ctx != NULL); - - printf("\nTesting against SSL connection (%s:%d):\n", cfg.ssl.host, cfg.ssl.port); - cfg.type = CONN_SSL; - - test_blocking_connection(cfg); - test_blocking_connection_timeouts(cfg); - test_blocking_io_errors(cfg); - test_invalid_timeout_errors(cfg); - test_append_formatted_commands(cfg); - if (throughput) test_throughput(cfg); - - redisFreeSSLContext(_ssl_ctx); - _ssl_ctx = NULL; - } -#endif - - if (test_inherit_fd) { - printf("\nTesting against inherited fd (%s): ", cfg.unix_sock.path); - if (test_unix_socket) { - printf("\n"); - cfg.type = CONN_FD; - test_blocking_connection(cfg); - } else { - test_skipped(); - } - } - - if (fails || (skips_as_fails && skips)) { - printf("*** %d TESTS FAILED ***\n", fails); - if (skips) { - printf("*** %d TESTS SKIPPED ***\n", skips); - } - return 1; - } - - printf("ALL TESTS PASSED (%d skipped)\n", skips); - return 0; -} diff --git a/ext/hiredis-1.0.2/test.sh b/ext/hiredis-1.0.2/test.sh deleted file mode 100755 index c72bcb0dc..000000000 --- a/ext/hiredis-1.0.2/test.sh +++ /dev/null @@ -1,78 +0,0 @@ -#!/bin/sh -ue - -REDIS_SERVER=${REDIS_SERVER:-redis-server} -REDIS_PORT=${REDIS_PORT:-56379} -REDIS_SSL_PORT=${REDIS_SSL_PORT:-56443} -TEST_SSL=${TEST_SSL:-0} -SKIPS_AS_FAILS=${SKIPS_AS_FAILS-:0} -SSL_TEST_ARGS= -SKIPS_ARG= - -tmpdir=$(mktemp -d) -PID_FILE=${tmpdir}/hiredis-test-redis.pid -SOCK_FILE=${tmpdir}/hiredis-test-redis.sock - -if [ "$TEST_SSL" = "1" ]; then - SSL_CA_CERT=${tmpdir}/ca.crt - SSL_CA_KEY=${tmpdir}/ca.key - SSL_CERT=${tmpdir}/redis.crt - SSL_KEY=${tmpdir}/redis.key - - openssl genrsa -out ${tmpdir}/ca.key 4096 - openssl req \ - -x509 -new -nodes -sha256 \ - -key ${SSL_CA_KEY} \ - -days 3650 \ - -subj '/CN=Hiredis Test CA' \ - -out ${SSL_CA_CERT} - openssl genrsa -out ${SSL_KEY} 2048 - openssl req \ - -new -sha256 \ - -key ${SSL_KEY} \ - -subj '/CN=Hiredis Test Cert' | \ - openssl x509 \ - -req -sha256 \ - -CA ${SSL_CA_CERT} \ - -CAkey ${SSL_CA_KEY} \ - -CAserial ${tmpdir}/ca.txt \ - -CAcreateserial \ - -days 365 \ - -out ${SSL_CERT} - - SSL_TEST_ARGS="--ssl-host 127.0.0.1 --ssl-port ${REDIS_SSL_PORT} --ssl-ca-cert ${SSL_CA_CERT} --ssl-cert ${SSL_CERT} --ssl-key ${SSL_KEY}" -fi - -cleanup() { - set +e - kill $(cat ${PID_FILE}) - rm -rf ${tmpdir} -} -trap cleanup INT TERM EXIT - -cat > ${tmpdir}/redis.conf <> ${tmpdir}/redis.conf < /* for struct timeval */ - -#ifndef inline -#define inline __inline -#endif - -#ifndef strcasecmp -#define strcasecmp stricmp -#endif - -#ifndef strncasecmp -#define strncasecmp strnicmp -#endif - -#ifndef va_copy -#define va_copy(d,s) ((d) = (s)) -#endif - -#ifndef snprintf -#define snprintf c99_snprintf - -__inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) -{ - int count = -1; - - if (size != 0) - count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); - if (count == -1) - count = _vscprintf(format, ap); - - return count; -} - -__inline int c99_snprintf(char* str, size_t size, const char* format, ...) -{ - int count; - va_list ap; - - va_start(ap, format); - count = c99_vsnprintf(str, size, format, ap); - va_end(ap); - - return count; -} -#endif -#endif /* _MSC_VER */ - -#ifdef _WIN32 -#define strerror_r(errno,buf,len) strerror_s(buf,len,errno) -#endif /* _WIN32 */ - -#endif /* _WIN32_HELPER_INCLUDE */ diff --git a/ext/inja/README.md b/ext/inja/README.md deleted file mode 100644 index 9d66a2c28..000000000 --- a/ext/inja/README.md +++ /dev/null @@ -1,391 +0,0 @@ -[
](https://github.com/pantor/inja/releases) - -

- - CI Status - - - - Documentation Status - - - - - - - - Github Releases - - - - Github Issues - - - - GitHub License - -

- -Inja is a template engine for modern C++, loosely inspired by [jinja](http://jinja.pocoo.org) for python. It has an easy and yet powerful template syntax with all variables, loops, conditions, includes, callbacks, and comments you need, nested and combined as you like. Inja uses the wonderful [json](https://github.com/nlohmann/json) library by nlohmann for data input. Most importantly, inja needs only two header files, which is (nearly) as trivial as integration in C++ can get. Of course, everything is tested on all relevant compilers. Here is what it looks like: - -```.cpp -json data; -data["name"] = "world"; - -inja::render("Hello {{ name }}!", data); // Returns "Hello world!" -``` - -## Integration - -Inja is a headers only library, which can be downloaded from the [releases](https://github.com/pantor/inja/releases) or directly from the `include/` or `single_include/` folder. Inja uses `nlohmann/json.hpp` (>= v3.8.0) as its single dependency, so make sure it can be included from `inja.hpp`. json can be downloaded [here](https://github.com/nlohmann/json/releases). Then integration is as easy as: - -```.cpp -#include - -// Just for convenience -using namespace inja; -``` - -If you are using the [Meson Build System](http://mesonbuild.com), then you can wrap this repository as a subproject. - -If you are using [Conan](https://conan.io) to manage your dependencies, have a look at [this repository](https://github.com/DEGoodmanWilson/conan-inja). Please file issues [here](https://github.com/DEGoodmanWilson/conan-inja/issues) if you experience problems with the packages. - -You can also integrate inja in your project using [Hunter](https://github.com/cpp-pm/hunter), a package manager for C++. - -If you are using [vcpkg](https://github.com/Microsoft/vcpkg) on your project for external dependencies, then you can use the [inja package](https://github.com/Microsoft/vcpkg/tree/master/ports/inja). Please see the vcpkg project for any issues regarding the packaging. - -If you are using [cget](https://cget.readthedocs.io/en/latest/), you can install the latest development version with `cget install pantor/inja`. A specific version can be installed with `cget install pantor/inja@v2.1.0`. - -On macOS, you can install inja via [Homebrew](https://formulae.brew.sh/formula/inja#default) and `brew install inja`. - -If you are using [conda](https://docs.conda.io/en/latest/), you can install the latest version from [conda-forge](https://anaconda.org/conda-forge/inja) with `conda install -c conda-forge inja`. - -## Tutorial - -This tutorial will give you an idea how to use inja. It will explain the most important concepts and give practical advices using examples and executable code. Beside this tutorial, you may check out the [documentation](https://pantor.github.io/inja). - -### Template Rendering - -The basic template rendering takes a template as a `std::string` and a `json` object for all data. It returns the rendered template as an `std::string`. - -```.cpp -json data; -data["name"] = "world"; - -render("Hello {{ name }}!", data); // Returns std::string "Hello world!" -render_to(std::cout, "Hello {{ name }}!", data); // Writes "Hello world!" to stream -``` - -For more advanced usage, an environment is recommended. -```.cpp -Environment env; - -// Render a string with json data -std::string result = env.render("Hello {{ name }}!", data); // "Hello world!" - -// Or directly read a template file -Template temp = env.parse_template("./templates/greeting.txt"); -std::string result = env.render(temp, data); // "Hello world!" - -data["name"] = "Inja"; -std::string result = env.render(temp, data); // "Hello Inja!" - -// Or read the template file (and/or the json file) directly from the environment -result = env.render_file("./templates/greeting.txt", data); -result = env.render_file_with_json_file("./templates/greeting.txt", "./data.json"); - -// Or write a rendered template file -env.write(temp, data, "./result.txt"); -env.write_with_json_file("./templates/greeting.txt", "./data.json", "./result.txt"); -``` - -The environment class can be configured to your needs. -```.cpp -// With default settings -Environment env_default; - -// With global path to template files and where files will be saved -Environment env_1 {"../path/templates/"}; - -// With separate input and output path -Environment env_2 {"../path/templates/", "../path/results/"}; - -// With other opening and closing strings (here the defaults) -env.set_expression("{{", "}}"); // Expressions -env.set_comment("{#", "#}"); // Comments -env.set_statement("{%", "%}"); // Statements {% %} for many things, see below -env.set_line_statement("##"); // Line statements ## (just an opener) -``` - -### Variables - -Variables are rendered within the `{{ ... }}` expressions. -```.cpp -json data; -data["neighbour"] = "Peter"; -data["guests"] = {"Jeff", "Tom", "Patrick"}; -data["time"]["start"] = 16; -data["time"]["end"] = 22; - -// Indexing in array -render("{{ guests.1 }}", data); // "Tom" - -// Objects -render("{{ time.start }} to {{ time.end + 1 }}pm", data); // "16 to 23pm" -``` -If no variable is found, valid JSON is printed directly, otherwise an `inja::RenderError` is thrown. - -### Statements - -Statements can be written either with the `{% ... %}` syntax or the `##` syntax for entire lines. Note that `##` needs to start the line without indentation. The most important statements are loops, conditions and file includes. All statements can be nested. - -#### Loops - -```.cpp -// Combining loops and line statements -render(R"(Guest List: -## for guest in guests - {{ loop.index1 }}: {{ guest }} -## endfor )", data) - -/* Guest List: - 1: Jeff - 2: Tom - 3: Patrick */ -``` -In a loop, the special variables `loop.index (number)`, `loop.index1 (number)`, `loop.is_first (boolean)` and `loop.is_last (boolean)` are defined. In nested loops, the parent loop variables are available e.g. via `loop.parent.index`. You can also iterate over objects like `{% for key, value in time %}`. - -#### Conditions - -Conditions support the typical if, else if and else statements. Following conditions are for example possible: -```.cpp -// Standard comparisons with a variable -render("{% if time.hour >= 20 %}Serve{% else if time.hour >= 18 %}Make{% endif %} dinner.", data); // Serve dinner. - -// Variable in list -render("{% if neighbour in guests %}Turn up the music!{% endif %}", data); // Turn up the music! - -// Logical operations -render("{% if guest_count < (3+2) and all_tired %}Sleepy...{% else %}Keep going...{% endif %}", data); // Sleepy... - -// Negations -render("{% if not guest_count %}The End{% endif %}", data); // The End -``` - -#### Includes - -You can either include other in-memory templates or from the file system. -```.cpp -// To include in-memory templates, add them to the environment first -inja::Template content_template = env.parse("Hello {{ neighbour }}!"); -env.include_template("content", content_template); -env.render("Content: {% include \"content\" %}", data); // "Content: Hello Peter!" - -// Other template files are included relative from the current file location -render("{% include \"footer.html\" %}", data); -``` -If a corresponding template could not be found in the file system, the *include callback* is called: -```.cpp -// The callback takes the current path and the wanted include name and returns a template -env.set_include_callback([&env](const std::string& path, const std::string& template_name) { - return env.parse("Hello {{ neighbour }} from " + template_name); -}); - -// You can disable to search for templates in the file system via -env.set_search_included_templates_in_files(false); -``` - -Inja will throw an `inja::RenderError` if an included file is not found and no callback is specified. To disable this error, you can call `env.set_throw_at_missing_includes(false)`. - -#### Assignments - -Variables can also be defined within the template using the set statment. -```.cpp -render("{% set new_hour=23 %}{{ new_hour }}pm", data); // "23pm" -render("{% set time.start=18 %}{{ time.start }}pm", data); // using json pointers -``` - -Assignments only set the value within the rendering context; they do not modify the json object passed into the `render` call. - -### Functions - -A few functions are implemented within the inja template syntax. They can be called with -```.cpp -// Upper and lower function, for string cases -render("Hello {{ upper(neighbour) }}!", data); // "Hello PETER!" -render("Hello {{ lower(neighbour) }}!", data); // "Hello peter!" - -// Range function, useful for loops -render("{% for i in range(4) %}{{ loop.index1 }}{% endfor %}", data); // "1234" -render("{% for i in range(3) %}{{ at(guests, i) }} {% endfor %}", data); // "Jeff Tom Patrick " - -// Length function (please don't combine with range, use list directly...) -render("I count {{ length(guests) }} guests.", data); // "I count 3 guests." - -// Get first and last element in a list -render("{{ first(guests) }} was first.", data); // "Jeff was first." -render("{{ last(guests) }} was last.", data); // "Patir was last." - -// Sort a list -render("{{ sort([3,2,1]) }}", data); // "[1,2,3]" -render("{{ sort(guests) }}", data); // "[\"Jeff\", \"Patrick\", \"Tom\"]" - -// Join a list with a separator -render("{{ join([1,2,3], \" + \") }}", data); // "1 + 2 + 3" -render("{{ join(guests, \", \") }}", data); // "Jeff, Patrick, Tom" - -// Round numbers to a given precision -render("{{ round(3.1415, 0) }}", data); // 3 -render("{{ round(3.1415, 3) }}", data); // 3.142 - -// Check if a value is odd, even or divisible by a number -render("{{ odd(42) }}", data); // false -render("{{ even(42) }}", data); // true -render("{{ divisibleBy(42, 7) }}", data); // true - -// Maximum and minimum values from a list -render("{{ max([1, 2, 3]) }}", data); // 3 -render("{{ min([-2.4, -1.2, 4.5]) }}", data); // -2.4 - -// Convert strings to numbers -render("{{ int(\"2\") == 2 }}", data); // true -render("{{ float(\"1.8\") > 2 }}", data); // false - -// Set default values if variables are not defined -render("Hello {{ default(neighbour, \"my friend\") }}!", data); // "Hello Peter!" -render("Hello {{ default(colleague, \"my friend\") }}!", data); // "Hello my friend!" - -// Access an objects value dynamically -render("{{ at(time, \"start\") }} to {{ time.end }}", data); // "16 to 22" - -// Check if a key exists in an object -render("{{ exists(\"guests\") }}", data); // "true" -render("{{ exists(\"city\") }}", data); // "false" -render("{{ existsIn(time, \"start\") }}", data); // "true" -render("{{ existsIn(time, neighbour) }}", data); // "false" - -// Check if a key is a specific type -render("{{ isString(neighbour) }}", data); // "true" -render("{{ isArray(guests) }}", data); // "true" -// Implemented type checks: isArray, isBoolean, isFloat, isInteger, isNumber, isObject, isString, -``` - -### Callbacks - -You can create your own and more complex functions with callbacks. These are implemented with `std::function`, so you can for example use C++ lambdas. Inja `Arguments` are a vector of json pointers. -```.cpp -Environment env; - -/* - * Callbacks are defined by its: - * - name, - * - (optional) number of arguments, - * - callback function. - */ -env.add_callback("double", 1, [](Arguments& args) { - int number = args.at(0)->get(); // Adapt the index and type of the argument - return 2 * number; -}); - -// You can then use a callback like a regular function -env.render("{{ double(16) }}", data); // "32" - -// Inja falls back to variadic callbacks if the number of expected arguments is omitted. -env.add_callback("argmax", [](Arguments& args) { - auto result = std::max_element(args.begin(), args.end(), [](const json* a, const json* b) { return *a < *b;}); - return std::distance(args.begin(), result); -}); -env.render("{{ argmax(4, 2, 6) }}", data); // "2" -env.render("{{ argmax(0, 2, 6, 8, 3) }}", data); // "3" - -// A callback without argument can be used like a dynamic variable: -std::string greet = "Hello"; -env.add_callback("double-greetings", 0, [greet](Arguments args) { - return greet + " " + greet + "!"; -}); -env.render("{{ double-greetings }}", data); // "Hello Hello!" -``` -You can also add a void callback without return variable, e.g. for debugging: -```.cpp -env.add_void_callback("log", 1, [greet](Arguments args) { - std::cout << "logging: " << args[0] << std::endl; -}); -env.render("{{ log(neighbour) }}", data); // Prints nothing to result, only to cout... -``` - -### Template Inheritance - -Template inheritance allows you to build a base *skeleton* template that contains all the common elements and defines blocks that child templates can override. Lets show an example: The base template -```.html - - - - {% block head %} - - {% block title %}{% endblock %} - My Webpage - {% endblock %} - - -
{% block content %}{% endblock %}
- - -``` -contains three `blocks` that child templates can fill in. The child template -```.html -{% extends "base.html" %} -{% block title %}Index{% endblock %} -{% block head %} - {{ super() }} - -{% endblock %} -{% block content %} -

Index

-

- Welcome to my blog! -

-{% endblock %} -``` -calls a parent template with the `extends` keyword; it should be the first element in the template. It is possible to render the contents of the parent block by calling `super()`. In the case of multiple levels of `{% extends %}`, super references may be called with an argument (e.g. `super(2)`) to skip levels in the inheritance tree. - -### Whitespace Control - -In the default configuration, no whitespace is removed while rendering the file. To support a more readable template style, you can configure the environment to control whitespaces before and after a statement automatically. While enabling `set_trim_blocks` removes the first newline after a statement, `set_lstrip_blocks` strips tabs and spaces from the beginning of a line to the start of a block. - -```.cpp -Environment env; -env.set_trim_blocks(true); -env.set_lstrip_blocks(true); -``` - -With both `trim_blocks` and `lstrip_blocks` enabled, you can put statements on their own lines. Furthermore, you can also strip whitespaces for both statements and expressions by hand. If you add a minus sign (`-`) to the start or end, the whitespaces before or after that block will be removed: - -```.cpp -render("Hello {{- name -}} !", data); // "Hello Inja!" -render("{% if neighbour in guests -%} I was there{% endif -%} !", data); // Renders without any whitespaces -``` - -Stripping behind a statement or expression also removes any newlines. - -### Comments - -Comments can be written with the `{# ... #}` syntax. -```.cpp -render("Hello{# Todo #}!", data); // "Hello!" -``` - -### Exceptions - -Inja uses exceptions to handle ill-formed template input. However, exceptions can be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `INJA_NOEXCEPTION`. In this case, exceptions are replaced by `abort()` calls. - - -## Supported compilers - -Inja uses the `string_view` feature of the C++17 STL. Currently, the following compilers are tested: - -- GCC 7 - 11 (and possibly later) -- Clang 5 - 12 (and possibly later) -- Microsoft Visual C++ 2017 15.0 - 2022 (and possibly later) - -A list of supported compiler / os versions can be found in the [CI definition](https://github.com/pantor/inja/blob/master/.github/workflows/ci.yml). diff --git a/ext/inja/inja.hpp b/ext/inja/inja.hpp deleted file mode 100644 index 5b469745f..000000000 --- a/ext/inja/inja.hpp +++ /dev/null @@ -1,2949 +0,0 @@ -/* - ___ _ Version 3.3 - |_ _|_ __ (_) __ _ https://github.com/pantor/inja - | || '_ \ | |/ _` | Licensed under the MIT License . - | || | | || | (_| | - |___|_| |_|/ |\__,_| Copyright (c) 2018-2021 Lars Berscheid - |__/ -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 INCLUDE_INJA_INJA_HPP_ -#define INCLUDE_INJA_INJA_HPP_ - -#include - -namespace inja { -#ifndef INJA_DATA_TYPE -using json = nlohmann::json; -#else -using json = INJA_DATA_TYPE; -#endif -} // namespace inja - -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(INJA_NOEXCEPTION) -#ifndef INJA_THROW -#define INJA_THROW(exception) throw exception -#endif -#else -#include -#ifndef INJA_THROW -#define INJA_THROW(exception) \ - std::abort(); \ - std::ignore = exception -#endif -#ifndef INJA_NOEXCEPTION -#define INJA_NOEXCEPTION -#endif -#endif - -// #include "environment.hpp" -#ifndef INCLUDE_INJA_ENVIRONMENT_HPP_ -#define INCLUDE_INJA_ENVIRONMENT_HPP_ - -#include -#include -#include -#include -#include -#include - -// #include "config.hpp" -#ifndef INCLUDE_INJA_CONFIG_HPP_ -#define INCLUDE_INJA_CONFIG_HPP_ - -#include -#include - -// #include "template.hpp" -#ifndef INCLUDE_INJA_TEMPLATE_HPP_ -#define INCLUDE_INJA_TEMPLATE_HPP_ - -#include -#include -#include -#include - -// #include "node.hpp" -#ifndef INCLUDE_INJA_NODE_HPP_ -#define INCLUDE_INJA_NODE_HPP_ - -#include -#include -#include - -// #include "function_storage.hpp" -#ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_ -#define INCLUDE_INJA_FUNCTION_STORAGE_HPP_ - -#include -#include - -namespace inja { - -using Arguments = std::vector; -using CallbackFunction = std::function; -using VoidCallbackFunction = std::function; - -/*! - * \brief Class for builtin functions and user-defined callbacks. - */ -class FunctionStorage { -public: - enum class Operation { - Not, - And, - Or, - In, - Equal, - NotEqual, - Greater, - GreaterEqual, - Less, - LessEqual, - Add, - Subtract, - Multiplication, - Division, - Power, - Modulo, - AtId, - At, - Default, - DivisibleBy, - Even, - Exists, - ExistsInObject, - First, - Float, - Int, - IsArray, - IsBoolean, - IsFloat, - IsInteger, - IsNumber, - IsObject, - IsString, - Last, - Length, - Lower, - Max, - Min, - Odd, - Range, - Round, - Sort, - Upper, - Super, - Join, - Callback, - ParenLeft, - ParenRight, - None, - }; - - struct FunctionData { - explicit FunctionData(const Operation& op, const CallbackFunction& cb = CallbackFunction {}): operation(op), callback(cb) {} - const Operation operation; - const CallbackFunction callback; - }; - -private: - const int VARIADIC {-1}; - - std::map, FunctionData> function_storage = { - {std::make_pair("at", 2), FunctionData {Operation::At}}, - {std::make_pair("default", 2), FunctionData {Operation::Default}}, - {std::make_pair("divisibleBy", 2), FunctionData {Operation::DivisibleBy}}, - {std::make_pair("even", 1), FunctionData {Operation::Even}}, - {std::make_pair("exists", 1), FunctionData {Operation::Exists}}, - {std::make_pair("existsIn", 2), FunctionData {Operation::ExistsInObject}}, - {std::make_pair("first", 1), FunctionData {Operation::First}}, - {std::make_pair("float", 1), FunctionData {Operation::Float}}, - {std::make_pair("int", 1), FunctionData {Operation::Int}}, - {std::make_pair("isArray", 1), FunctionData {Operation::IsArray}}, - {std::make_pair("isBoolean", 1), FunctionData {Operation::IsBoolean}}, - {std::make_pair("isFloat", 1), FunctionData {Operation::IsFloat}}, - {std::make_pair("isInteger", 1), FunctionData {Operation::IsInteger}}, - {std::make_pair("isNumber", 1), FunctionData {Operation::IsNumber}}, - {std::make_pair("isObject", 1), FunctionData {Operation::IsObject}}, - {std::make_pair("isString", 1), FunctionData {Operation::IsString}}, - {std::make_pair("last", 1), FunctionData {Operation::Last}}, - {std::make_pair("length", 1), FunctionData {Operation::Length}}, - {std::make_pair("lower", 1), FunctionData {Operation::Lower}}, - {std::make_pair("max", 1), FunctionData {Operation::Max}}, - {std::make_pair("min", 1), FunctionData {Operation::Min}}, - {std::make_pair("odd", 1), FunctionData {Operation::Odd}}, - {std::make_pair("range", 1), FunctionData {Operation::Range}}, - {std::make_pair("round", 2), FunctionData {Operation::Round}}, - {std::make_pair("sort", 1), FunctionData {Operation::Sort}}, - {std::make_pair("upper", 1), FunctionData {Operation::Upper}}, - {std::make_pair("super", 0), FunctionData {Operation::Super}}, - {std::make_pair("super", 1), FunctionData {Operation::Super}}, - {std::make_pair("join", 2), FunctionData {Operation::Join}}, - }; - -public: - void add_builtin(std::string_view name, int num_args, Operation op) { - function_storage.emplace(std::make_pair(static_cast(name), num_args), FunctionData {op}); - } - - void add_callback(std::string_view name, int num_args, const CallbackFunction& callback) { - function_storage.emplace(std::make_pair(static_cast(name), num_args), FunctionData {Operation::Callback, callback}); - } - - FunctionData find_function(std::string_view name, int num_args) const { - auto it = function_storage.find(std::make_pair(static_cast(name), num_args)); - if (it != function_storage.end()) { - return it->second; - - // Find variadic function - } else if (num_args > 0) { - it = function_storage.find(std::make_pair(static_cast(name), VARIADIC)); - if (it != function_storage.end()) { - return it->second; - } - } - - return FunctionData {Operation::None}; - } -}; - -} // namespace inja - -#endif // INCLUDE_INJA_FUNCTION_STORAGE_HPP_ - -// #include "utils.hpp" -#ifndef INCLUDE_INJA_UTILS_HPP_ -#define INCLUDE_INJA_UTILS_HPP_ - -#include -#include -#include -#include -#include - -// #include "exceptions.hpp" -#ifndef INCLUDE_INJA_EXCEPTIONS_HPP_ -#define INCLUDE_INJA_EXCEPTIONS_HPP_ - -#include -#include - -namespace inja { - -struct SourceLocation { - size_t line; - size_t column; -}; - -struct InjaError : public std::runtime_error { - const std::string type; - const std::string message; - - const SourceLocation location; - - explicit InjaError(const std::string& type, const std::string& message) - : std::runtime_error("[inja.exception." + type + "] " + message), type(type), message(message), location({0, 0}) {} - - explicit InjaError(const std::string& type, const std::string& message, SourceLocation location) - : std::runtime_error("[inja.exception." + type + "] (at " + std::to_string(location.line) + ":" + std::to_string(location.column) + ") " + message), - type(type), message(message), location(location) {} -}; - -struct ParserError : public InjaError { - explicit ParserError(const std::string& message, SourceLocation location): InjaError("parser_error", message, location) {} -}; - -struct RenderError : public InjaError { - explicit RenderError(const std::string& message, SourceLocation location): InjaError("render_error", message, location) {} -}; - -struct FileError : public InjaError { - explicit FileError(const std::string& message): InjaError("file_error", message) {} - explicit FileError(const std::string& message, SourceLocation location): InjaError("file_error", message, location) {} -}; - -struct DataError : public InjaError { - explicit DataError(const std::string& message, SourceLocation location): InjaError("data_error", message, location) {} -}; - -} // namespace inja - -#endif // INCLUDE_INJA_EXCEPTIONS_HPP_ - - -namespace inja { - -namespace string_view { -inline std::string_view slice(std::string_view view, size_t start, size_t end) { - start = std::min(start, view.size()); - end = std::min(std::max(start, end), view.size()); - return view.substr(start, end - start); -} - -inline std::pair split(std::string_view view, char Separator) { - size_t idx = view.find(Separator); - if (idx == std::string_view::npos) { - return std::make_pair(view, std::string_view()); - } - return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, std::string_view::npos)); -} - -inline bool starts_with(std::string_view view, std::string_view prefix) { - return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0); -} -} // namespace string_view - -inline SourceLocation get_source_location(std::string_view content, size_t pos) { - // Get line and offset position (starts at 1:1) - auto sliced = string_view::slice(content, 0, pos); - std::size_t last_newline = sliced.rfind("\n"); - - if (last_newline == std::string_view::npos) { - return {1, sliced.length() + 1}; - } - - // Count newlines - size_t count_lines = 0; - size_t search_start = 0; - while (search_start <= sliced.size()) { - search_start = sliced.find("\n", search_start) + 1; - if (search_start == 0) { - break; - } - count_lines += 1; - } - - return {count_lines + 1, sliced.length() - last_newline}; -} - -inline void replace_substring(std::string& s, const std::string& f, const std::string& t) { - if (f.empty()) { - return; - } - for (auto pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find next occurrence of f - {} -} - -} // namespace inja - -#endif // INCLUDE_INJA_UTILS_HPP_ - - -namespace inja { - -class NodeVisitor; -class BlockNode; -class TextNode; -class ExpressionNode; -class LiteralNode; -class DataNode; -class FunctionNode; -class ExpressionListNode; -class StatementNode; -class ForStatementNode; -class ForArrayStatementNode; -class ForObjectStatementNode; -class IfStatementNode; -class IncludeStatementNode; -class ExtendsStatementNode; -class BlockStatementNode; -class SetStatementNode; - -class NodeVisitor { -public: - virtual ~NodeVisitor() = default; - - virtual void visit(const BlockNode& node) = 0; - virtual void visit(const TextNode& node) = 0; - virtual void visit(const ExpressionNode& node) = 0; - virtual void visit(const LiteralNode& node) = 0; - virtual void visit(const DataNode& node) = 0; - virtual void visit(const FunctionNode& node) = 0; - virtual void visit(const ExpressionListNode& node) = 0; - virtual void visit(const StatementNode& node) = 0; - virtual void visit(const ForStatementNode& node) = 0; - virtual void visit(const ForArrayStatementNode& node) = 0; - virtual void visit(const ForObjectStatementNode& node) = 0; - virtual void visit(const IfStatementNode& node) = 0; - virtual void visit(const IncludeStatementNode& node) = 0; - virtual void visit(const ExtendsStatementNode& node) = 0; - virtual void visit(const BlockStatementNode& node) = 0; - virtual void visit(const SetStatementNode& node) = 0; -}; - -/*! - * \brief Base node class for the abstract syntax tree (AST). - */ -class AstNode { -public: - virtual void accept(NodeVisitor& v) const = 0; - - size_t pos; - - AstNode(size_t pos): pos(pos) {} - virtual ~AstNode() {} -}; - -class BlockNode : public AstNode { -public: - std::vector> nodes; - - explicit BlockNode(): AstNode(0) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class TextNode : public AstNode { -public: - const size_t length; - - explicit TextNode(size_t pos, size_t length): AstNode(pos), length(length) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class ExpressionNode : public AstNode { -public: - explicit ExpressionNode(size_t pos): AstNode(pos) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class LiteralNode : public ExpressionNode { -public: - const json value; - - explicit LiteralNode(std::string_view data_text, size_t pos): ExpressionNode(pos), value(json::parse(data_text)) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class DataNode : public ExpressionNode { -public: - const std::string name; - const json::json_pointer ptr; - - static std::string convert_dot_to_ptr(std::string_view ptr_name) { - std::string result; - do { - std::string_view part; - std::tie(part, ptr_name) = string_view::split(ptr_name, '.'); - result.push_back('/'); - result.append(part.begin(), part.end()); - } while (!ptr_name.empty()); - return result; - } - - explicit DataNode(std::string_view ptr_name, size_t pos): ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_ptr(ptr_name))) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class FunctionNode : public ExpressionNode { - using Op = FunctionStorage::Operation; - -public: - enum class Associativity { - Left, - Right, - }; - - unsigned int precedence; - Associativity associativity; - - Op operation; - - std::string name; - int number_args; // Should also be negative -> -1 for unknown number - std::vector> arguments; - CallbackFunction callback; - - explicit FunctionNode(std::string_view name, size_t pos) - : ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) {} - explicit FunctionNode(Op operation, size_t pos): ExpressionNode(pos), operation(operation), number_args(1) { - switch (operation) { - case Op::Not: { - number_args = 1; - precedence = 4; - associativity = Associativity::Left; - } break; - case Op::And: { - number_args = 2; - precedence = 1; - associativity = Associativity::Left; - } break; - case Op::Or: { - number_args = 2; - precedence = 1; - associativity = Associativity::Left; - } break; - case Op::In: { - number_args = 2; - precedence = 2; - associativity = Associativity::Left; - } break; - case Op::Equal: { - number_args = 2; - precedence = 2; - associativity = Associativity::Left; - } break; - case Op::NotEqual: { - number_args = 2; - precedence = 2; - associativity = Associativity::Left; - } break; - case Op::Greater: { - number_args = 2; - precedence = 2; - associativity = Associativity::Left; - } break; - case Op::GreaterEqual: { - number_args = 2; - precedence = 2; - associativity = Associativity::Left; - } break; - case Op::Less: { - number_args = 2; - precedence = 2; - associativity = Associativity::Left; - } break; - case Op::LessEqual: { - number_args = 2; - precedence = 2; - associativity = Associativity::Left; - } break; - case Op::Add: { - number_args = 2; - precedence = 3; - associativity = Associativity::Left; - } break; - case Op::Subtract: { - number_args = 2; - precedence = 3; - associativity = Associativity::Left; - } break; - case Op::Multiplication: { - number_args = 2; - precedence = 4; - associativity = Associativity::Left; - } break; - case Op::Division: { - number_args = 2; - precedence = 4; - associativity = Associativity::Left; - } break; - case Op::Power: { - number_args = 2; - precedence = 5; - associativity = Associativity::Right; - } break; - case Op::Modulo: { - number_args = 2; - precedence = 4; - associativity = Associativity::Left; - } break; - case Op::AtId: { - number_args = 2; - precedence = 8; - associativity = Associativity::Left; - } break; - default: { - precedence = 1; - associativity = Associativity::Left; - } - } - } - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class ExpressionListNode : public AstNode { -public: - std::shared_ptr root; - - explicit ExpressionListNode(): AstNode(0) {} - explicit ExpressionListNode(size_t pos): AstNode(pos) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class StatementNode : public AstNode { -public: - StatementNode(size_t pos): AstNode(pos) {} - - virtual void accept(NodeVisitor& v) const = 0; -}; - -class ForStatementNode : public StatementNode { -public: - ExpressionListNode condition; - BlockNode body; - BlockNode* const parent; - - ForStatementNode(BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent) {} - - virtual void accept(NodeVisitor& v) const = 0; -}; - -class ForArrayStatementNode : public ForStatementNode { -public: - const std::string value; - - explicit ForArrayStatementNode(const std::string& value, BlockNode* const parent, size_t pos): ForStatementNode(parent, pos), value(value) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class ForObjectStatementNode : public ForStatementNode { -public: - const std::string key; - const std::string value; - - explicit ForObjectStatementNode(const std::string& key, const std::string& value, BlockNode* const parent, size_t pos) - : ForStatementNode(parent, pos), key(key), value(value) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class IfStatementNode : public StatementNode { -public: - ExpressionListNode condition; - BlockNode true_statement; - BlockNode false_statement; - BlockNode* const parent; - - const bool is_nested; - bool has_false_statement {false}; - - explicit IfStatementNode(BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent), is_nested(false) {} - explicit IfStatementNode(bool is_nested, BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent), is_nested(is_nested) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class IncludeStatementNode : public StatementNode { -public: - const std::string file; - - explicit IncludeStatementNode(const std::string& file, size_t pos): StatementNode(pos), file(file) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -class ExtendsStatementNode : public StatementNode { -public: - const std::string file; - - explicit ExtendsStatementNode(const std::string& file, size_t pos): StatementNode(pos), file(file) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - }; -}; - -class BlockStatementNode : public StatementNode { -public: - const std::string name; - BlockNode block; - BlockNode* const parent; - - explicit BlockStatementNode(BlockNode* const parent, const std::string& name, size_t pos): StatementNode(pos), name(name), parent(parent) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - }; -}; - -class SetStatementNode : public StatementNode { -public: - const std::string key; - ExpressionListNode expression; - - explicit SetStatementNode(const std::string& key, size_t pos): StatementNode(pos), key(key) {} - - void accept(NodeVisitor& v) const { - v.visit(*this); - } -}; - -} // namespace inja - -#endif // INCLUDE_INJA_NODE_HPP_ - -// #include "statistics.hpp" -#ifndef INCLUDE_INJA_STATISTICS_HPP_ -#define INCLUDE_INJA_STATISTICS_HPP_ - -// #include "node.hpp" - - -namespace inja { - -/*! - * \brief A class for counting statistics on a Template. - */ -class StatisticsVisitor : public NodeVisitor { - void visit(const BlockNode& node) { - for (auto& n : node.nodes) { - n->accept(*this); - } - } - - void visit(const TextNode&) {} - void visit(const ExpressionNode&) {} - void visit(const LiteralNode&) {} - - void visit(const DataNode&) { - variable_counter += 1; - } - - void visit(const FunctionNode& node) { - for (auto& n : node.arguments) { - n->accept(*this); - } - } - - void visit(const ExpressionListNode& node) { - node.root->accept(*this); - } - - void visit(const StatementNode&) {} - void visit(const ForStatementNode&) {} - - void visit(const ForArrayStatementNode& node) { - node.condition.accept(*this); - node.body.accept(*this); - } - - void visit(const ForObjectStatementNode& node) { - node.condition.accept(*this); - node.body.accept(*this); - } - - void visit(const IfStatementNode& node) { - node.condition.accept(*this); - node.true_statement.accept(*this); - node.false_statement.accept(*this); - } - - void visit(const IncludeStatementNode&) {} - - void visit(const ExtendsStatementNode&) {} - - void visit(const BlockStatementNode& node) { - node.block.accept(*this); - } - - void visit(const SetStatementNode&) {} - -public: - unsigned int variable_counter; - - explicit StatisticsVisitor(): variable_counter(0) {} -}; - -} // namespace inja - -#endif // INCLUDE_INJA_STATISTICS_HPP_ - - -namespace inja { - -/*! - * \brief The main inja Template. - */ -struct Template { - BlockNode root; - std::string content; - std::map> block_storage; - - explicit Template() {} - explicit Template(const std::string& content): content(content) {} - - /// Return number of variables (total number, not distinct ones) in the template - int count_variables() { - auto statistic_visitor = StatisticsVisitor(); - root.accept(statistic_visitor); - return statistic_visitor.variable_counter; - } -}; - -using TemplateStorage = std::map; - -} // namespace inja - -#endif // INCLUDE_INJA_TEMPLATE_HPP_ - - -namespace inja { - -/*! - * \brief Class for lexer configuration. - */ -struct LexerConfig { - std::string statement_open {"{%"}; - std::string statement_open_no_lstrip {"{%+"}; - std::string statement_open_force_lstrip {"{%-"}; - std::string statement_close {"%}"}; - std::string statement_close_force_rstrip {"-%}"}; - std::string line_statement {"##"}; - std::string expression_open {"{{"}; - std::string expression_open_force_lstrip {"{{-"}; - std::string expression_close {"}}"}; - std::string expression_close_force_rstrip {"-}}"}; - std::string comment_open {"{#"}; - std::string comment_open_force_lstrip {"{#-"}; - std::string comment_close {"#}"}; - std::string comment_close_force_rstrip {"-#}"}; - std::string open_chars {"#{"}; - - bool trim_blocks {false}; - bool lstrip_blocks {false}; - - void update_open_chars() { - open_chars = ""; - if (open_chars.find(line_statement[0]) == std::string::npos) { - open_chars += line_statement[0]; - } - if (open_chars.find(statement_open[0]) == std::string::npos) { - open_chars += statement_open[0]; - } - if (open_chars.find(statement_open_no_lstrip[0]) == std::string::npos) { - open_chars += statement_open_no_lstrip[0]; - } - if (open_chars.find(statement_open_force_lstrip[0]) == std::string::npos) { - open_chars += statement_open_force_lstrip[0]; - } - if (open_chars.find(expression_open[0]) == std::string::npos) { - open_chars += expression_open[0]; - } - if (open_chars.find(expression_open_force_lstrip[0]) == std::string::npos) { - open_chars += expression_open_force_lstrip[0]; - } - if (open_chars.find(comment_open[0]) == std::string::npos) { - open_chars += comment_open[0]; - } - if (open_chars.find(comment_open_force_lstrip[0]) == std::string::npos) { - open_chars += comment_open_force_lstrip[0]; - } - } -}; - -/*! - * \brief Class for parser configuration. - */ -struct ParserConfig { - bool search_included_templates_in_files {true}; - - std::function include_callback; -}; - -/*! - * \brief Class for render configuration. - */ -struct RenderConfig { - bool throw_at_missing_includes {true}; -}; - -} // namespace inja - -#endif // INCLUDE_INJA_CONFIG_HPP_ - -// #include "function_storage.hpp" - -// #include "parser.hpp" -#ifndef INCLUDE_INJA_PARSER_HPP_ -#define INCLUDE_INJA_PARSER_HPP_ - -#include -#include -#include -#include -#include - -// #include "config.hpp" - -// #include "exceptions.hpp" - -// #include "function_storage.hpp" - -// #include "lexer.hpp" -#ifndef INCLUDE_INJA_LEXER_HPP_ -#define INCLUDE_INJA_LEXER_HPP_ - -#include -#include - -// #include "config.hpp" - -// #include "token.hpp" -#ifndef INCLUDE_INJA_TOKEN_HPP_ -#define INCLUDE_INJA_TOKEN_HPP_ - -#include -#include - -namespace inja { - -/*! - * \brief Helper-class for the inja Lexer. - */ -struct Token { - enum class Kind { - Text, - ExpressionOpen, // {{ - ExpressionClose, // }} - LineStatementOpen, // ## - LineStatementClose, // \n - StatementOpen, // {% - StatementClose, // %} - CommentOpen, // {# - CommentClose, // #} - Id, // this, this.foo - Number, // 1, 2, -1, 5.2, -5.3 - String, // "this" - Plus, // + - Minus, // - - Times, // * - Slash, // / - Percent, // % - Power, // ^ - Comma, // , - Dot, // . - Colon, // : - LeftParen, // ( - RightParen, // ) - LeftBracket, // [ - RightBracket, // ] - LeftBrace, // { - RightBrace, // } - Equal, // == - NotEqual, // != - GreaterThan, // > - GreaterEqual, // >= - LessThan, // < - LessEqual, // <= - Unknown, - Eof, - }; - - Kind kind {Kind::Unknown}; - std::string_view text; - - explicit constexpr Token() = default; - explicit constexpr Token(Kind kind, std::string_view text): kind(kind), text(text) {} - - std::string describe() const { - switch (kind) { - case Kind::Text: - return ""; - case Kind::LineStatementClose: - return ""; - case Kind::Eof: - return ""; - default: - return static_cast(text); - } - } -}; - -} // namespace inja - -#endif // INCLUDE_INJA_TOKEN_HPP_ - -// #include "utils.hpp" - - -namespace inja { - -/*! - * \brief Class for lexing an inja Template. - */ -class Lexer { - enum class State { - Text, - ExpressionStart, - ExpressionStartForceLstrip, - ExpressionBody, - LineStart, - LineBody, - StatementStart, - StatementStartNoLstrip, - StatementStartForceLstrip, - StatementBody, - CommentStart, - CommentStartForceLstrip, - CommentBody, - }; - - enum class MinusState { - Operator, - Number, - }; - - const LexerConfig& config; - - State state; - MinusState minus_state; - std::string_view m_in; - size_t tok_start; - size_t pos; - - Token scan_body(std::string_view close, Token::Kind closeKind, std::string_view close_trim = std::string_view(), bool trim = false) { - again: - // skip whitespace (except for \n as it might be a close) - if (tok_start >= m_in.size()) { - return make_token(Token::Kind::Eof); - } - const char ch = m_in[tok_start]; - if (ch == ' ' || ch == '\t' || ch == '\r') { - tok_start += 1; - goto again; - } - - // check for close - if (!close_trim.empty() && inja::string_view::starts_with(m_in.substr(tok_start), close_trim)) { - state = State::Text; - pos = tok_start + close_trim.size(); - const Token tok = make_token(closeKind); - skip_whitespaces_and_newlines(); - return tok; - } - - if (inja::string_view::starts_with(m_in.substr(tok_start), close)) { - state = State::Text; - pos = tok_start + close.size(); - const Token tok = make_token(closeKind); - if (trim) { - skip_whitespaces_and_first_newline(); - } - return tok; - } - - // skip \n - if (ch == '\n') { - tok_start += 1; - goto again; - } - - pos = tok_start + 1; - if (std::isalpha(ch)) { - minus_state = MinusState::Operator; - return scan_id(); - } - - const MinusState current_minus_state = minus_state; - if (minus_state == MinusState::Operator) { - minus_state = MinusState::Number; - } - - switch (ch) { - case '+': - return make_token(Token::Kind::Plus); - case '-': - if (current_minus_state == MinusState::Operator) { - return make_token(Token::Kind::Minus); - } - return scan_number(); - case '*': - return make_token(Token::Kind::Times); - case '/': - return make_token(Token::Kind::Slash); - case '^': - return make_token(Token::Kind::Power); - case '%': - return make_token(Token::Kind::Percent); - case '.': - return make_token(Token::Kind::Dot); - case ',': - return make_token(Token::Kind::Comma); - case ':': - return make_token(Token::Kind::Colon); - case '(': - return make_token(Token::Kind::LeftParen); - case ')': - minus_state = MinusState::Operator; - return make_token(Token::Kind::RightParen); - case '[': - return make_token(Token::Kind::LeftBracket); - case ']': - minus_state = MinusState::Operator; - return make_token(Token::Kind::RightBracket); - case '{': - return make_token(Token::Kind::LeftBrace); - case '}': - minus_state = MinusState::Operator; - return make_token(Token::Kind::RightBrace); - case '>': - if (pos < m_in.size() && m_in[pos] == '=') { - pos += 1; - return make_token(Token::Kind::GreaterEqual); - } - return make_token(Token::Kind::GreaterThan); - case '<': - if (pos < m_in.size() && m_in[pos] == '=') { - pos += 1; - return make_token(Token::Kind::LessEqual); - } - return make_token(Token::Kind::LessThan); - case '=': - if (pos < m_in.size() && m_in[pos] == '=') { - pos += 1; - return make_token(Token::Kind::Equal); - } - return make_token(Token::Kind::Unknown); - case '!': - if (pos < m_in.size() && m_in[pos] == '=') { - pos += 1; - return make_token(Token::Kind::NotEqual); - } - return make_token(Token::Kind::Unknown); - case '\"': - return scan_string(); - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - minus_state = MinusState::Operator; - return scan_number(); - case '_': - case '@': - case '$': - minus_state = MinusState::Operator; - return scan_id(); - default: - return make_token(Token::Kind::Unknown); - } - } - - Token scan_id() { - for (;;) { - if (pos >= m_in.size()) { - break; - } - const char ch = m_in[pos]; - if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') { - break; - } - pos += 1; - } - return make_token(Token::Kind::Id); - } - - Token scan_number() { - for (;;) { - if (pos >= m_in.size()) { - break; - } - const char ch = m_in[pos]; - // be very permissive in lexer (we'll catch errors when conversion happens) - if (!(std::isdigit(ch) || ch == '.' || ch == 'e' || ch == 'E' || (ch == '+' && (pos == 0 || m_in[pos-1] == 'e' || m_in[pos-1] == 'E')) || (ch == '-' && (pos == 0 || m_in[pos-1] == 'e' || m_in[pos-1] == 'E')))) { - break; - } - pos += 1; - } - return make_token(Token::Kind::Number); - } - - Token scan_string() { - bool escape {false}; - for (;;) { - if (pos >= m_in.size()) { - break; - } - const char ch = m_in[pos++]; - if (ch == '\\') { - escape = true; - } else if (!escape && ch == m_in[tok_start]) { - break; - } else { - escape = false; - } - } - return make_token(Token::Kind::String); - } - - Token make_token(Token::Kind kind) const { - return Token(kind, string_view::slice(m_in, tok_start, pos)); - } - - void skip_whitespaces_and_newlines() { - if (pos < m_in.size()) { - while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t' || m_in[pos] == '\n' || m_in[pos] == '\r')) { - pos += 1; - } - } - } - - void skip_whitespaces_and_first_newline() { - if (pos < m_in.size()) { - while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t')) { - pos += 1; - } - } - - if (pos < m_in.size()) { - const char ch = m_in[pos]; - if (ch == '\n') { - pos += 1; - } else if (ch == '\r') { - pos += 1; - if (pos < m_in.size() && m_in[pos] == '\n') { - pos += 1; - } - } - } - } - - static std::string_view clear_final_line_if_whitespace(std::string_view text) { - std::string_view result = text; - while (!result.empty()) { - const char ch = result.back(); - if (ch == ' ' || ch == '\t') { - result.remove_suffix(1); - } else if (ch == '\n' || ch == '\r') { - break; - } else { - return text; - } - } - return result; - } - -public: - explicit Lexer(const LexerConfig& config): config(config), state(State::Text), minus_state(MinusState::Number) {} - - SourceLocation current_position() const { - return get_source_location(m_in, tok_start); - } - - void start(std::string_view input) { - m_in = input; - tok_start = 0; - pos = 0; - state = State::Text; - minus_state = MinusState::Number; - - // Consume byte order mark (BOM) for UTF-8 - if (inja::string_view::starts_with(m_in, "\xEF\xBB\xBF")) { - m_in = m_in.substr(3); - } - } - - Token scan() { - tok_start = pos; - - again: - if (tok_start >= m_in.size()) { - return make_token(Token::Kind::Eof); - } - - switch (state) { - default: - case State::Text: { - // fast-scan to first open character - const size_t open_start = m_in.substr(pos).find_first_of(config.open_chars); - if (open_start == std::string_view::npos) { - // didn't find open, return remaining text as text token - pos = m_in.size(); - return make_token(Token::Kind::Text); - } - pos += open_start; - - // try to match one of the opening sequences, and get the close - std::string_view open_str = m_in.substr(pos); - bool must_lstrip = false; - if (inja::string_view::starts_with(open_str, config.expression_open)) { - if (inja::string_view::starts_with(open_str, config.expression_open_force_lstrip)) { - state = State::ExpressionStartForceLstrip; - must_lstrip = true; - } else { - state = State::ExpressionStart; - } - } else if (inja::string_view::starts_with(open_str, config.statement_open)) { - if (inja::string_view::starts_with(open_str, config.statement_open_no_lstrip)) { - state = State::StatementStartNoLstrip; - } else if (inja::string_view::starts_with(open_str, config.statement_open_force_lstrip)) { - state = State::StatementStartForceLstrip; - must_lstrip = true; - } else { - state = State::StatementStart; - must_lstrip = config.lstrip_blocks; - } - } else if (inja::string_view::starts_with(open_str, config.comment_open)) { - if (inja::string_view::starts_with(open_str, config.comment_open_force_lstrip)) { - state = State::CommentStartForceLstrip; - must_lstrip = true; - } else { - state = State::CommentStart; - must_lstrip = config.lstrip_blocks; - } - } else if ((pos == 0 || m_in[pos - 1] == '\n') && inja::string_view::starts_with(open_str, config.line_statement)) { - state = State::LineStart; - } else { - pos += 1; // wasn't actually an opening sequence - goto again; - } - - std::string_view text = string_view::slice(m_in, tok_start, pos); - if (must_lstrip) { - text = clear_final_line_if_whitespace(text); - } - - if (text.empty()) { - goto again; // don't generate empty token - } - return Token(Token::Kind::Text, text); - } - case State::ExpressionStart: { - state = State::ExpressionBody; - pos += config.expression_open.size(); - return make_token(Token::Kind::ExpressionOpen); - } - case State::ExpressionStartForceLstrip: { - state = State::ExpressionBody; - pos += config.expression_open_force_lstrip.size(); - return make_token(Token::Kind::ExpressionOpen); - } - case State::LineStart: { - state = State::LineBody; - pos += config.line_statement.size(); - return make_token(Token::Kind::LineStatementOpen); - } - case State::StatementStart: { - state = State::StatementBody; - pos += config.statement_open.size(); - return make_token(Token::Kind::StatementOpen); - } - case State::StatementStartNoLstrip: { - state = State::StatementBody; - pos += config.statement_open_no_lstrip.size(); - return make_token(Token::Kind::StatementOpen); - } - case State::StatementStartForceLstrip: { - state = State::StatementBody; - pos += config.statement_open_force_lstrip.size(); - return make_token(Token::Kind::StatementOpen); - } - case State::CommentStart: { - state = State::CommentBody; - pos += config.comment_open.size(); - return make_token(Token::Kind::CommentOpen); - } - case State::CommentStartForceLstrip: { - state = State::CommentBody; - pos += config.comment_open_force_lstrip.size(); - return make_token(Token::Kind::CommentOpen); - } - case State::ExpressionBody: - return scan_body(config.expression_close, Token::Kind::ExpressionClose, config.expression_close_force_rstrip); - case State::LineBody: - return scan_body("\n", Token::Kind::LineStatementClose); - case State::StatementBody: - return scan_body(config.statement_close, Token::Kind::StatementClose, config.statement_close_force_rstrip, config.trim_blocks); - case State::CommentBody: { - // fast-scan to comment close - const size_t end = m_in.substr(pos).find(config.comment_close); - if (end == std::string_view::npos) { - pos = m_in.size(); - return make_token(Token::Kind::Eof); - } - - // Check for trim pattern - const bool must_rstrip = inja::string_view::starts_with(m_in.substr(pos + end - 1), config.comment_close_force_rstrip); - - // return the entire comment in the close token - state = State::Text; - pos += end + config.comment_close.size(); - Token tok = make_token(Token::Kind::CommentClose); - - if (must_rstrip || config.trim_blocks) { - skip_whitespaces_and_first_newline(); - } - return tok; - } - } - } - - const LexerConfig& get_config() const { - return config; - } -}; - -} // namespace inja - -#endif // INCLUDE_INJA_LEXER_HPP_ - -// #include "node.hpp" - -// #include "template.hpp" - -// #include "token.hpp" - -// #include "utils.hpp" - - -namespace inja { - -/*! - * \brief Class for parsing an inja Template. - */ -class Parser { - const ParserConfig& config; - - Lexer lexer; - TemplateStorage& template_storage; - const FunctionStorage& function_storage; - - Token tok, peek_tok; - bool have_peek_tok {false}; - - size_t current_paren_level {0}; - size_t current_bracket_level {0}; - size_t current_brace_level {0}; - - std::string_view literal_start; - - BlockNode* current_block {nullptr}; - ExpressionListNode* current_expression_list {nullptr}; - std::stack> function_stack; - std::vector> arguments; - - std::stack> operator_stack; - std::stack if_statement_stack; - std::stack for_statement_stack; - std::stack block_statement_stack; - - inline void throw_parser_error(const std::string& message) const { - INJA_THROW(ParserError(message, lexer.current_position())); - } - - inline void get_next_token() { - if (have_peek_tok) { - tok = peek_tok; - have_peek_tok = false; - } else { - tok = lexer.scan(); - } - } - - inline void get_peek_token() { - if (!have_peek_tok) { - peek_tok = lexer.scan(); - have_peek_tok = true; - } - } - - inline void add_literal(const char* content_ptr) { - std::string_view data_text(literal_start.data(), tok.text.data() - literal_start.data() + tok.text.size()); - arguments.emplace_back(std::make_shared(data_text, data_text.data() - content_ptr)); - } - - inline void add_operator() { - auto function = operator_stack.top(); - operator_stack.pop(); - - for (int i = 0; i < function->number_args; ++i) { - function->arguments.insert(function->arguments.begin(), arguments.back()); - arguments.pop_back(); - } - arguments.emplace_back(function); - } - - void add_to_template_storage(std::string_view path, std::string& template_name) { - if (template_storage.find(template_name) != template_storage.end()) { - return; - } - - std::string original_path = static_cast(path); - std::string original_name = template_name; - - if (config.search_included_templates_in_files) { - // Build the relative path - template_name = original_path + original_name; - if (template_name.compare(0, 2, "./") == 0) { - template_name.erase(0, 2); - } - - if (template_storage.find(template_name) == template_storage.end()) { - // Load file - std::ifstream file; - file.open(template_name); - if (!file.fail()) { - std::string text((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - - auto include_template = Template(text); - template_storage.emplace(template_name, include_template); - parse_into_template(template_storage[template_name], template_name); - return; - } else if (!config.include_callback) { - INJA_THROW(FileError("failed accessing file at '" + template_name + "'")); - } - } - } - - // Try include callback - if (config.include_callback) { - auto include_template = config.include_callback(original_path, original_name); - template_storage.emplace(template_name, include_template); - } - } - - std::string parse_filename(const Token& tok) const { - if (tok.kind != Token::Kind::String) { - throw_parser_error("expected string, got '" + tok.describe() + "'"); - } - - if (tok.text.length() < 2) { - throw_parser_error("expected filename, got '" + static_cast(tok.text) + "'"); - } - - // Remove first and last character "" - return std::string {tok.text.substr(1, tok.text.length() - 2)}; - } - - bool parse_expression(Template& tmpl, Token::Kind closing) { - while (tok.kind != closing && tok.kind != Token::Kind::Eof) { - // Literals - switch (tok.kind) { - case Token::Kind::String: { - if (current_brace_level == 0 && current_bracket_level == 0) { - literal_start = tok.text; - add_literal(tmpl.content.c_str()); - } - } break; - case Token::Kind::Number: { - if (current_brace_level == 0 && current_bracket_level == 0) { - literal_start = tok.text; - add_literal(tmpl.content.c_str()); - } - } break; - case Token::Kind::LeftBracket: { - if (current_brace_level == 0 && current_bracket_level == 0) { - literal_start = tok.text; - } - current_bracket_level += 1; - } break; - case Token::Kind::LeftBrace: { - if (current_brace_level == 0 && current_bracket_level == 0) { - literal_start = tok.text; - } - current_brace_level += 1; - } break; - case Token::Kind::RightBracket: { - if (current_bracket_level == 0) { - throw_parser_error("unexpected ']'"); - } - - current_bracket_level -= 1; - if (current_brace_level == 0 && current_bracket_level == 0) { - add_literal(tmpl.content.c_str()); - } - } break; - case Token::Kind::RightBrace: { - if (current_brace_level == 0) { - throw_parser_error("unexpected '}'"); - } - - current_brace_level -= 1; - if (current_brace_level == 0 && current_bracket_level == 0) { - add_literal(tmpl.content.c_str()); - } - } break; - case Token::Kind::Id: { - get_peek_token(); - - // Data Literal - if (tok.text == static_cast("true") || tok.text == static_cast("false") || - tok.text == static_cast("null")) { - if (current_brace_level == 0 && current_bracket_level == 0) { - literal_start = tok.text; - add_literal(tmpl.content.c_str()); - } - - // Operator - } else if (tok.text == "and" || tok.text == "or" || tok.text == "in" || tok.text == "not") { - goto parse_operator; - - // Functions - } else if (peek_tok.kind == Token::Kind::LeftParen) { - operator_stack.emplace(std::make_shared(static_cast(tok.text), tok.text.data() - tmpl.content.c_str())); - function_stack.emplace(operator_stack.top().get(), current_paren_level); - - // Variables - } else { - arguments.emplace_back(std::make_shared(static_cast(tok.text), tok.text.data() - tmpl.content.c_str())); - } - - // Operators - } break; - case Token::Kind::Equal: - case Token::Kind::NotEqual: - case Token::Kind::GreaterThan: - case Token::Kind::GreaterEqual: - case Token::Kind::LessThan: - case Token::Kind::LessEqual: - case Token::Kind::Plus: - case Token::Kind::Minus: - case Token::Kind::Times: - case Token::Kind::Slash: - case Token::Kind::Power: - case Token::Kind::Percent: - case Token::Kind::Dot: { - - parse_operator: - FunctionStorage::Operation operation; - switch (tok.kind) { - case Token::Kind::Id: { - if (tok.text == "and") { - operation = FunctionStorage::Operation::And; - } else if (tok.text == "or") { - operation = FunctionStorage::Operation::Or; - } else if (tok.text == "in") { - operation = FunctionStorage::Operation::In; - } else if (tok.text == "not") { - operation = FunctionStorage::Operation::Not; - } else { - throw_parser_error("unknown operator in parser."); - } - } break; - case Token::Kind::Equal: { - operation = FunctionStorage::Operation::Equal; - } break; - case Token::Kind::NotEqual: { - operation = FunctionStorage::Operation::NotEqual; - } break; - case Token::Kind::GreaterThan: { - operation = FunctionStorage::Operation::Greater; - } break; - case Token::Kind::GreaterEqual: { - operation = FunctionStorage::Operation::GreaterEqual; - } break; - case Token::Kind::LessThan: { - operation = FunctionStorage::Operation::Less; - } break; - case Token::Kind::LessEqual: { - operation = FunctionStorage::Operation::LessEqual; - } break; - case Token::Kind::Plus: { - operation = FunctionStorage::Operation::Add; - } break; - case Token::Kind::Minus: { - operation = FunctionStorage::Operation::Subtract; - } break; - case Token::Kind::Times: { - operation = FunctionStorage::Operation::Multiplication; - } break; - case Token::Kind::Slash: { - operation = FunctionStorage::Operation::Division; - } break; - case Token::Kind::Power: { - operation = FunctionStorage::Operation::Power; - } break; - case Token::Kind::Percent: { - operation = FunctionStorage::Operation::Modulo; - } break; - case Token::Kind::Dot: { - operation = FunctionStorage::Operation::AtId; - } break; - default: { - throw_parser_error("unknown operator in parser."); - } - } - auto function_node = std::make_shared(operation, tok.text.data() - tmpl.content.c_str()); - - while (!operator_stack.empty() && - ((operator_stack.top()->precedence > function_node->precedence) || - (operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left)) && - (operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft)) { - add_operator(); - } - - operator_stack.emplace(function_node); - } break; - case Token::Kind::Comma: { - if (current_brace_level == 0 && current_bracket_level == 0) { - if (function_stack.empty()) { - throw_parser_error("unexpected ','"); - } - - function_stack.top().first->number_args += 1; - } - } break; - case Token::Kind::Colon: { - if (current_brace_level == 0 && current_bracket_level == 0) { - throw_parser_error("unexpected ':'"); - } - } break; - case Token::Kind::LeftParen: { - current_paren_level += 1; - operator_stack.emplace(std::make_shared(FunctionStorage::Operation::ParenLeft, tok.text.data() - tmpl.content.c_str())); - - get_peek_token(); - if (peek_tok.kind == Token::Kind::RightParen) { - if (!function_stack.empty() && function_stack.top().second == current_paren_level - 1) { - function_stack.top().first->number_args = 0; - } - } - } break; - case Token::Kind::RightParen: { - current_paren_level -= 1; - while (!operator_stack.empty() && operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft) { - add_operator(); - } - - if (!operator_stack.empty() && operator_stack.top()->operation == FunctionStorage::Operation::ParenLeft) { - operator_stack.pop(); - } - - if (!function_stack.empty() && function_stack.top().second == current_paren_level) { - auto func = function_stack.top().first; - auto function_data = function_storage.find_function(func->name, func->number_args); - if (function_data.operation == FunctionStorage::Operation::None) { - throw_parser_error("unknown function " + func->name); - } - func->operation = function_data.operation; - if (function_data.operation == FunctionStorage::Operation::Callback) { - func->callback = function_data.callback; - } - - if (operator_stack.empty()) { - throw_parser_error("internal error at function " + func->name); - } - - add_operator(); - function_stack.pop(); - } - } - default: - break; - } - - get_next_token(); - } - - while (!operator_stack.empty()) { - add_operator(); - } - - if (arguments.size() == 1) { - current_expression_list->root = arguments[0]; - arguments = {}; - } else if (arguments.size() > 1) { - throw_parser_error("malformed expression"); - } - - return true; - } - - bool parse_statement(Template& tmpl, Token::Kind closing, std::string_view path) { - if (tok.kind != Token::Kind::Id) { - return false; - } - - if (tok.text == static_cast("if")) { - get_next_token(); - - auto if_statement_node = std::make_shared(current_block, tok.text.data() - tmpl.content.c_str()); - current_block->nodes.emplace_back(if_statement_node); - if_statement_stack.emplace(if_statement_node.get()); - current_block = &if_statement_node->true_statement; - current_expression_list = &if_statement_node->condition; - - if (!parse_expression(tmpl, closing)) { - return false; - } - } else if (tok.text == static_cast("else")) { - if (if_statement_stack.empty()) { - throw_parser_error("else without matching if"); - } - auto& if_statement_data = if_statement_stack.top(); - get_next_token(); - - if_statement_data->has_false_statement = true; - current_block = &if_statement_data->false_statement; - - // Chained else if - if (tok.kind == Token::Kind::Id && tok.text == static_cast("if")) { - get_next_token(); - - auto if_statement_node = std::make_shared(true, current_block, tok.text.data() - tmpl.content.c_str()); - current_block->nodes.emplace_back(if_statement_node); - if_statement_stack.emplace(if_statement_node.get()); - current_block = &if_statement_node->true_statement; - current_expression_list = &if_statement_node->condition; - - if (!parse_expression(tmpl, closing)) { - return false; - } - } - } else if (tok.text == static_cast("endif")) { - if (if_statement_stack.empty()) { - throw_parser_error("endif without matching if"); - } - - // Nested if statements - while (if_statement_stack.top()->is_nested) { - if_statement_stack.pop(); - } - - auto& if_statement_data = if_statement_stack.top(); - get_next_token(); - - current_block = if_statement_data->parent; - if_statement_stack.pop(); - } else if (tok.text == static_cast("block")) { - get_next_token(); - - if (tok.kind != Token::Kind::Id) { - throw_parser_error("expected block name, got '" + tok.describe() + "'"); - } - - const std::string block_name = static_cast(tok.text); - - auto block_statement_node = std::make_shared(current_block, block_name, tok.text.data() - tmpl.content.c_str()); - current_block->nodes.emplace_back(block_statement_node); - block_statement_stack.emplace(block_statement_node.get()); - current_block = &block_statement_node->block; - auto success = tmpl.block_storage.emplace(block_name, block_statement_node); - if (!success.second) { - throw_parser_error("block with the name '" + block_name + "' does already exist"); - } - - get_next_token(); - } else if (tok.text == static_cast("endblock")) { - if (block_statement_stack.empty()) { - throw_parser_error("endblock without matching block"); - } - - auto& block_statement_data = block_statement_stack.top(); - get_next_token(); - - current_block = block_statement_data->parent; - block_statement_stack.pop(); - } else if (tok.text == static_cast("for")) { - get_next_token(); - - // options: for a in arr; for a, b in obj - if (tok.kind != Token::Kind::Id) { - throw_parser_error("expected id, got '" + tok.describe() + "'"); - } - - Token value_token = tok; - get_next_token(); - - // Object type - std::shared_ptr for_statement_node; - if (tok.kind == Token::Kind::Comma) { - get_next_token(); - if (tok.kind != Token::Kind::Id) { - throw_parser_error("expected id, got '" + tok.describe() + "'"); - } - - Token key_token = std::move(value_token); - value_token = tok; - get_next_token(); - - for_statement_node = std::make_shared(static_cast(key_token.text), static_cast(value_token.text), - current_block, tok.text.data() - tmpl.content.c_str()); - - // Array type - } else { - for_statement_node = - std::make_shared(static_cast(value_token.text), current_block, tok.text.data() - tmpl.content.c_str()); - } - - current_block->nodes.emplace_back(for_statement_node); - for_statement_stack.emplace(for_statement_node.get()); - current_block = &for_statement_node->body; - current_expression_list = &for_statement_node->condition; - - if (tok.kind != Token::Kind::Id || tok.text != static_cast("in")) { - throw_parser_error("expected 'in', got '" + tok.describe() + "'"); - } - get_next_token(); - - if (!parse_expression(tmpl, closing)) { - return false; - } - } else if (tok.text == static_cast("endfor")) { - if (for_statement_stack.empty()) { - throw_parser_error("endfor without matching for"); - } - - auto& for_statement_data = for_statement_stack.top(); - get_next_token(); - - current_block = for_statement_data->parent; - for_statement_stack.pop(); - } else if (tok.text == static_cast("include")) { - get_next_token(); - - std::string template_name = parse_filename(tok); - add_to_template_storage(path, template_name); - - current_block->nodes.emplace_back(std::make_shared(template_name, tok.text.data() - tmpl.content.c_str())); - - get_next_token(); - } else if (tok.text == static_cast("extends")) { - get_next_token(); - - std::string template_name = parse_filename(tok); - add_to_template_storage(path, template_name); - - current_block->nodes.emplace_back(std::make_shared(template_name, tok.text.data() - tmpl.content.c_str())); - - get_next_token(); - } else if (tok.text == static_cast("set")) { - get_next_token(); - - if (tok.kind != Token::Kind::Id) { - throw_parser_error("expected variable name, got '" + tok.describe() + "'"); - } - - std::string key = static_cast(tok.text); - get_next_token(); - - auto set_statement_node = std::make_shared(key, tok.text.data() - tmpl.content.c_str()); - current_block->nodes.emplace_back(set_statement_node); - current_expression_list = &set_statement_node->expression; - - if (tok.text != static_cast("=")) { - throw_parser_error("expected '=', got '" + tok.describe() + "'"); - } - get_next_token(); - - if (!parse_expression(tmpl, closing)) { - return false; - } - } else { - return false; - } - return true; - } - - void parse_into(Template& tmpl, std::string_view path) { - lexer.start(tmpl.content); - current_block = &tmpl.root; - - for (;;) { - get_next_token(); - switch (tok.kind) { - case Token::Kind::Eof: { - if (!if_statement_stack.empty()) { - throw_parser_error("unmatched if"); - } - if (!for_statement_stack.empty()) { - throw_parser_error("unmatched for"); - } - } - return; - case Token::Kind::Text: { - current_block->nodes.emplace_back(std::make_shared(tok.text.data() - tmpl.content.c_str(), tok.text.size())); - } break; - case Token::Kind::StatementOpen: { - get_next_token(); - if (!parse_statement(tmpl, Token::Kind::StatementClose, path)) { - throw_parser_error("expected statement, got '" + tok.describe() + "'"); - } - if (tok.kind != Token::Kind::StatementClose) { - throw_parser_error("expected statement close, got '" + tok.describe() + "'"); - } - } break; - case Token::Kind::LineStatementOpen: { - get_next_token(); - if (!parse_statement(tmpl, Token::Kind::LineStatementClose, path)) { - throw_parser_error("expected statement, got '" + tok.describe() + "'"); - } - if (tok.kind != Token::Kind::LineStatementClose && tok.kind != Token::Kind::Eof) { - throw_parser_error("expected line statement close, got '" + tok.describe() + "'"); - } - } break; - case Token::Kind::ExpressionOpen: { - get_next_token(); - - auto expression_list_node = std::make_shared(tok.text.data() - tmpl.content.c_str()); - current_block->nodes.emplace_back(expression_list_node); - current_expression_list = expression_list_node.get(); - - if (!parse_expression(tmpl, Token::Kind::ExpressionClose)) { - throw_parser_error("expected expression, got '" + tok.describe() + "'"); - } - - if (tok.kind != Token::Kind::ExpressionClose) { - throw_parser_error("expected expression close, got '" + tok.describe() + "'"); - } - } break; - case Token::Kind::CommentOpen: { - get_next_token(); - if (tok.kind != Token::Kind::CommentClose) { - throw_parser_error("expected comment close, got '" + tok.describe() + "'"); - } - } break; - default: { - throw_parser_error("unexpected token '" + tok.describe() + "'"); - } break; - } - } - } - -public: - explicit Parser(const ParserConfig& parser_config, const LexerConfig& lexer_config, TemplateStorage& template_storage, - const FunctionStorage& function_storage) - : config(parser_config), lexer(lexer_config), template_storage(template_storage), function_storage(function_storage) {} - - Template parse(std::string_view input, std::string_view path) { - auto result = Template(static_cast(input)); - parse_into(result, path); - return result; - } - - Template parse(std::string_view input) { - return parse(input, "./"); - } - - void parse_into_template(Template& tmpl, std::string_view filename) { - std::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1); - - // StringRef path = sys::path::parent_path(filename); - auto sub_parser = Parser(config, lexer.get_config(), template_storage, function_storage); - sub_parser.parse_into(tmpl, path); - } - - std::string load_file(const std::string& filename) { - std::ifstream file; - file.open(filename); - if (file.fail()) { - INJA_THROW(FileError("failed accessing file at '" + filename + "'")); - } - std::string text((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - return text; - } -}; - -} // namespace inja - -#endif // INCLUDE_INJA_PARSER_HPP_ - -// #include "renderer.hpp" -#ifndef INCLUDE_INJA_RENDERER_HPP_ -#define INCLUDE_INJA_RENDERER_HPP_ - -#include -#include -#include -#include -#include - -// #include "config.hpp" - -// #include "exceptions.hpp" - -// #include "node.hpp" - -// #include "template.hpp" - -// #include "utils.hpp" - - -namespace inja { - -/*! - * \brief Class for rendering a Template with data. - */ -class Renderer : public NodeVisitor { - using Op = FunctionStorage::Operation; - - const RenderConfig config; - const TemplateStorage& template_storage; - const FunctionStorage& function_storage; - - const Template* current_template; - size_t current_level {0}; - std::vector template_stack; - std::vector block_statement_stack; - - const json* data_input; - std::ostream* output_stream; - - json additional_data; - json* current_loop_data = &additional_data["loop"]; - - std::vector> data_tmp_stack; - std::stack data_eval_stack; - std::stack not_found_stack; - - bool break_rendering {false}; - - static bool truthy(const json* data) { - if (data->is_boolean()) { - return data->get(); - } else if (data->is_number()) { - return (*data != 0); - } else if (data->is_null()) { - return false; - } - return !data->empty(); - } - - void print_data(const std::shared_ptr value) { - if (value->is_string()) { - *output_stream << value->get_ref(); - } else if (value->is_number_integer()) { - *output_stream << value->get(); - } else if (value->is_null()) { - } else { - *output_stream << value->dump(); - } - } - - const std::shared_ptr eval_expression_list(const ExpressionListNode& expression_list) { - if (!expression_list.root) { - throw_renderer_error("empty expression", expression_list); - } - - expression_list.root->accept(*this); - - if (data_eval_stack.empty()) { - throw_renderer_error("empty expression", expression_list); - } else if (data_eval_stack.size() != 1) { - throw_renderer_error("malformed expression", expression_list); - } - - const auto result = data_eval_stack.top(); - data_eval_stack.pop(); - - if (!result) { - if (not_found_stack.empty()) { - throw_renderer_error("expression could not be evaluated", expression_list); - } - - auto node = not_found_stack.top(); - not_found_stack.pop(); - - throw_renderer_error("variable '" + static_cast(node->name) + "' not found", *node); - } - return std::make_shared(*result); - } - - void throw_renderer_error(const std::string& message, const AstNode& node) { - SourceLocation loc = get_source_location(current_template->content, node.pos); - INJA_THROW(RenderError(message, loc)); - } - - void make_result(const json&& result) { - auto result_ptr = std::make_shared(result); - data_tmp_stack.push_back(result_ptr); - data_eval_stack.push(result_ptr.get()); - } - - template std::array get_arguments(const FunctionNode& node) { - if (node.arguments.size() < N_start + N) { - throw_renderer_error("function needs " + std::to_string(N_start + N) + " variables, but has only found " + std::to_string(node.arguments.size()), node); - } - - for (size_t i = N_start; i < N_start + N; i += 1) { - node.arguments[i]->accept(*this); - } - - if (data_eval_stack.size() < N) { - throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(data_eval_stack.size()), node); - } - - std::array result; - for (size_t i = 0; i < N; i += 1) { - result[N - i - 1] = data_eval_stack.top(); - data_eval_stack.pop(); - - if (!result[N - i - 1]) { - const auto data_node = not_found_stack.top(); - not_found_stack.pop(); - - if (throw_not_found) { - throw_renderer_error("variable '" + static_cast(data_node->name) + "' not found", *data_node); - } - } - } - return result; - } - - template Arguments get_argument_vector(const FunctionNode& node) { - const size_t N = node.arguments.size(); - for (auto a : node.arguments) { - a->accept(*this); - } - - if (data_eval_stack.size() < N) { - throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(data_eval_stack.size()), node); - } - - Arguments result {N}; - for (size_t i = 0; i < N; i += 1) { - result[N - i - 1] = data_eval_stack.top(); - data_eval_stack.pop(); - - if (!result[N - i - 1]) { - const auto data_node = not_found_stack.top(); - not_found_stack.pop(); - - if (throw_not_found) { - throw_renderer_error("variable '" + static_cast(data_node->name) + "' not found", *data_node); - } - } - } - return result; - } - - void visit(const BlockNode& node) { - for (auto& n : node.nodes) { - n->accept(*this); - - if (break_rendering) { - break; - } - } - } - - void visit(const TextNode& node) { - output_stream->write(current_template->content.c_str() + node.pos, node.length); - } - - void visit(const ExpressionNode&) {} - - void visit(const LiteralNode& node) { - data_eval_stack.push(&node.value); - } - - void visit(const DataNode& node) { - if (additional_data.contains(node.ptr)) { - data_eval_stack.push(&(additional_data[node.ptr])); - } else if (data_input->contains(node.ptr)) { - data_eval_stack.push(&(*data_input)[node.ptr]); - } else { - // Try to evaluate as a no-argument callback - const auto function_data = function_storage.find_function(node.name, 0); - if (function_data.operation == FunctionStorage::Operation::Callback) { - Arguments empty_args {}; - const auto value = std::make_shared(function_data.callback(empty_args)); - data_tmp_stack.push_back(value); - data_eval_stack.push(value.get()); - } else { - data_eval_stack.push(nullptr); - not_found_stack.emplace(&node); - } - } - } - - void visit(const FunctionNode& node) { - switch (node.operation) { - case Op::Not: { - const auto args = get_arguments<1>(node); - make_result(!truthy(args[0])); - } break; - case Op::And: { - make_result(truthy(get_arguments<1, 0>(node)[0]) && truthy(get_arguments<1, 1>(node)[0])); - } break; - case Op::Or: { - make_result(truthy(get_arguments<1, 0>(node)[0]) || truthy(get_arguments<1, 1>(node)[0])); - } break; - case Op::In: { - const auto args = get_arguments<2>(node); - make_result(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end()); - } break; - case Op::Equal: { - const auto args = get_arguments<2>(node); - make_result(*args[0] == *args[1]); - } break; - case Op::NotEqual: { - const auto args = get_arguments<2>(node); - make_result(*args[0] != *args[1]); - } break; - case Op::Greater: { - const auto args = get_arguments<2>(node); - make_result(*args[0] > *args[1]); - } break; - case Op::GreaterEqual: { - const auto args = get_arguments<2>(node); - make_result(*args[0] >= *args[1]); - } break; - case Op::Less: { - const auto args = get_arguments<2>(node); - make_result(*args[0] < *args[1]); - } break; - case Op::LessEqual: { - const auto args = get_arguments<2>(node); - make_result(*args[0] <= *args[1]); - } break; - case Op::Add: { - const auto args = get_arguments<2>(node); - if (args[0]->is_string() && args[1]->is_string()) { - make_result(args[0]->get_ref() + args[1]->get_ref()); - } else if (args[0]->is_number_integer() && args[1]->is_number_integer()) { - make_result(args[0]->get() + args[1]->get()); - } else { - make_result(args[0]->get() + args[1]->get()); - } - } break; - case Op::Subtract: { - const auto args = get_arguments<2>(node); - if (args[0]->is_number_integer() && args[1]->is_number_integer()) { - make_result(args[0]->get() - args[1]->get()); - } else { - make_result(args[0]->get() - args[1]->get()); - } - } break; - case Op::Multiplication: { - const auto args = get_arguments<2>(node); - if (args[0]->is_number_integer() && args[1]->is_number_integer()) { - make_result(args[0]->get() * args[1]->get()); - } else { - make_result(args[0]->get() * args[1]->get()); - } - } break; - case Op::Division: { - const auto args = get_arguments<2>(node); - if (args[1]->get() == 0) { - throw_renderer_error("division by zero", node); - } - make_result(args[0]->get() / args[1]->get()); - } break; - case Op::Power: { - const auto args = get_arguments<2>(node); - if (args[0]->is_number_integer() && args[1]->get() >= 0) { - int result = static_cast(std::pow(args[0]->get(), args[1]->get())); - make_result(result); - } else { - double result = std::pow(args[0]->get(), args[1]->get()); - make_result(result); - } - } break; - case Op::Modulo: { - const auto args = get_arguments<2>(node); - make_result(args[0]->get() % args[1]->get()); - } break; - case Op::AtId: { - const auto container = get_arguments<1, 0, false>(node)[0]; - node.arguments[1]->accept(*this); - if (not_found_stack.empty()) { - throw_renderer_error("could not find element with given name", node); - } - const auto id_node = not_found_stack.top(); - not_found_stack.pop(); - data_eval_stack.pop(); - data_eval_stack.push(&container->at(id_node->name)); - } break; - case Op::At: { - const auto args = get_arguments<2>(node); - if (args[0]->is_object()) { - data_eval_stack.push(&args[0]->at(args[1]->get())); - } else { - data_eval_stack.push(&args[0]->at(args[1]->get())); - } - } break; - case Op::Default: { - const auto test_arg = get_arguments<1, 0, false>(node)[0]; - data_eval_stack.push(test_arg ? test_arg : get_arguments<1, 1>(node)[0]); - } break; - case Op::DivisibleBy: { - const auto args = get_arguments<2>(node); - const int divisor = args[1]->get(); - make_result((divisor != 0) && (args[0]->get() % divisor == 0)); - } break; - case Op::Even: { - make_result(get_arguments<1>(node)[0]->get() % 2 == 0); - } break; - case Op::Exists: { - auto&& name = get_arguments<1>(node)[0]->get_ref(); - make_result(data_input->contains(json::json_pointer(DataNode::convert_dot_to_ptr(name)))); - } break; - case Op::ExistsInObject: { - const auto args = get_arguments<2>(node); - auto&& name = args[1]->get_ref(); - make_result(args[0]->find(name) != args[0]->end()); - } break; - case Op::First: { - const auto result = &get_arguments<1>(node)[0]->front(); - data_eval_stack.push(result); - } break; - case Op::Float: { - make_result(std::stod(get_arguments<1>(node)[0]->get_ref())); - } break; - case Op::Int: { - make_result(std::stoi(get_arguments<1>(node)[0]->get_ref())); - } break; - case Op::Last: { - const auto result = &get_arguments<1>(node)[0]->back(); - data_eval_stack.push(result); - } break; - case Op::Length: { - const auto val = get_arguments<1>(node)[0]; - if (val->is_string()) { - make_result(val->get_ref().length()); - } else { - make_result(val->size()); - } - } break; - case Op::Lower: { - std::string result = get_arguments<1>(node)[0]->get(); - std::transform(result.begin(), result.end(), result.begin(), ::tolower); - make_result(std::move(result)); - } break; - case Op::Max: { - const auto args = get_arguments<1>(node); - const auto result = std::max_element(args[0]->begin(), args[0]->end()); - data_eval_stack.push(&(*result)); - } break; - case Op::Min: { - const auto args = get_arguments<1>(node); - const auto result = std::min_element(args[0]->begin(), args[0]->end()); - data_eval_stack.push(&(*result)); - } break; - case Op::Odd: { - make_result(get_arguments<1>(node)[0]->get() % 2 != 0); - } break; - case Op::Range: { - std::vector result(get_arguments<1>(node)[0]->get()); - std::iota(result.begin(), result.end(), 0); - make_result(std::move(result)); - } break; - case Op::Round: { - const auto args = get_arguments<2>(node); - const int precision = args[1]->get(); - const double result = std::round(args[0]->get() * std::pow(10.0, precision)) / std::pow(10.0, precision); - if (precision == 0) { - make_result(int(result)); - } else { - make_result(result); - } - } break; - case Op::Sort: { - auto result_ptr = std::make_shared(get_arguments<1>(node)[0]->get>()); - std::sort(result_ptr->begin(), result_ptr->end()); - data_tmp_stack.push_back(result_ptr); - data_eval_stack.push(result_ptr.get()); - } break; - case Op::Upper: { - std::string result = get_arguments<1>(node)[0]->get(); - std::transform(result.begin(), result.end(), result.begin(), ::toupper); - make_result(std::move(result)); - } break; - case Op::IsBoolean: { - make_result(get_arguments<1>(node)[0]->is_boolean()); - } break; - case Op::IsNumber: { - make_result(get_arguments<1>(node)[0]->is_number()); - } break; - case Op::IsInteger: { - make_result(get_arguments<1>(node)[0]->is_number_integer()); - } break; - case Op::IsFloat: { - make_result(get_arguments<1>(node)[0]->is_number_float()); - } break; - case Op::IsObject: { - make_result(get_arguments<1>(node)[0]->is_object()); - } break; - case Op::IsArray: { - make_result(get_arguments<1>(node)[0]->is_array()); - } break; - case Op::IsString: { - make_result(get_arguments<1>(node)[0]->is_string()); - } break; - case Op::Callback: { - auto args = get_argument_vector(node); - make_result(node.callback(args)); - } break; - case Op::Super: { - const auto args = get_argument_vector(node); - const size_t old_level = current_level; - const size_t level_diff = (args.size() == 1) ? args[0]->get() : 1; - const size_t level = current_level + level_diff; - - if (block_statement_stack.empty()) { - throw_renderer_error("super() call is not within a block", node); - } - - if (level < 1 || level > template_stack.size() - 1) { - throw_renderer_error("level of super() call does not match parent templates (between 1 and " + std::to_string(template_stack.size() - 1) + ")", node); - } - - const auto current_block_statement = block_statement_stack.back(); - const Template* new_template = template_stack.at(level); - const Template* old_template = current_template; - const auto block_it = new_template->block_storage.find(current_block_statement->name); - if (block_it != new_template->block_storage.end()) { - current_template = new_template; - current_level = level; - block_it->second->block.accept(*this); - current_level = old_level; - current_template = old_template; - } else { - throw_renderer_error("could not find block with name '" + current_block_statement->name + "'", node); - } - make_result(nullptr); - } break; - case Op::Join: { - const auto args = get_arguments<2>(node); - const auto separator = args[1]->get(); - std::ostringstream os; - std::string sep; - for (const auto& value : *args[0]) { - os << sep; - if (value.is_string()) { - os << value.get(); // otherwise the value is surrounded with "" - } else { - os << value.dump(); - } - sep = separator; - } - make_result(os.str()); - } break; - case Op::ParenLeft: - case Op::ParenRight: - case Op::None: - break; - } - } - - void visit(const ExpressionListNode& node) { - print_data(eval_expression_list(node)); - } - - void visit(const StatementNode&) {} - - void visit(const ForStatementNode&) {} - - void visit(const ForArrayStatementNode& node) { - const auto result = eval_expression_list(node.condition); - if (!result->is_array()) { - throw_renderer_error("object must be an array", node); - } - - if (!current_loop_data->empty()) { - auto tmp = *current_loop_data; // Because of clang-3 - (*current_loop_data)["parent"] = std::move(tmp); - } - - size_t index = 0; - (*current_loop_data)["is_first"] = true; - (*current_loop_data)["is_last"] = (result->size() <= 1); - for (auto it = result->begin(); it != result->end(); ++it) { - additional_data[static_cast(node.value)] = *it; - - (*current_loop_data)["index"] = index; - (*current_loop_data)["index1"] = index + 1; - if (index == 1) { - (*current_loop_data)["is_first"] = false; - } - if (index == result->size() - 1) { - (*current_loop_data)["is_last"] = true; - } - - node.body.accept(*this); - ++index; - } - - additional_data[static_cast(node.value)].clear(); - if (!(*current_loop_data)["parent"].empty()) { - const auto tmp = (*current_loop_data)["parent"]; - *current_loop_data = std::move(tmp); - } else { - current_loop_data = &additional_data["loop"]; - } - } - - void visit(const ForObjectStatementNode& node) { - const auto result = eval_expression_list(node.condition); - if (!result->is_object()) { - throw_renderer_error("object must be an object", node); - } - - if (!current_loop_data->empty()) { - (*current_loop_data)["parent"] = std::move(*current_loop_data); - } - - size_t index = 0; - (*current_loop_data)["is_first"] = true; - (*current_loop_data)["is_last"] = (result->size() <= 1); - for (auto it = result->begin(); it != result->end(); ++it) { - additional_data[static_cast(node.key)] = it.key(); - additional_data[static_cast(node.value)] = it.value(); - - (*current_loop_data)["index"] = index; - (*current_loop_data)["index1"] = index + 1; - if (index == 1) { - (*current_loop_data)["is_first"] = false; - } - if (index == result->size() - 1) { - (*current_loop_data)["is_last"] = true; - } - - node.body.accept(*this); - ++index; - } - - additional_data[static_cast(node.key)].clear(); - additional_data[static_cast(node.value)].clear(); - if (!(*current_loop_data)["parent"].empty()) { - *current_loop_data = std::move((*current_loop_data)["parent"]); - } else { - current_loop_data = &additional_data["loop"]; - } - } - - void visit(const IfStatementNode& node) { - const auto result = eval_expression_list(node.condition); - if (truthy(result.get())) { - node.true_statement.accept(*this); - } else if (node.has_false_statement) { - node.false_statement.accept(*this); - } - } - - void visit(const IncludeStatementNode& node) { - auto sub_renderer = Renderer(config, template_storage, function_storage); - const auto included_template_it = template_storage.find(node.file); - if (included_template_it != template_storage.end()) { - sub_renderer.render_to(*output_stream, included_template_it->second, *data_input, &additional_data); - } else if (config.throw_at_missing_includes) { - throw_renderer_error("include '" + node.file + "' not found", node); - } - } - - void visit(const ExtendsStatementNode& node) { - const auto included_template_it = template_storage.find(node.file); - if (included_template_it != template_storage.end()) { - const Template* parent_template = &included_template_it->second; - render_to(*output_stream, *parent_template, *data_input, &additional_data); - break_rendering = true; - } else if (config.throw_at_missing_includes) { - throw_renderer_error("extends '" + node.file + "' not found", node); - } - } - - void visit(const BlockStatementNode& node) { - const size_t old_level = current_level; - current_level = 0; - current_template = template_stack.front(); - const auto block_it = current_template->block_storage.find(node.name); - if (block_it != current_template->block_storage.end()) { - block_statement_stack.emplace_back(&node); - block_it->second->block.accept(*this); - block_statement_stack.pop_back(); - } - current_level = old_level; - current_template = template_stack.back(); - } - - void visit(const SetStatementNode& node) { - std::string ptr = node.key; - replace_substring(ptr, ".", "/"); - ptr = "/" + ptr; - additional_data[json::json_pointer(ptr)] = *eval_expression_list(node.expression); - } - -public: - Renderer(const RenderConfig& config, const TemplateStorage& template_storage, const FunctionStorage& function_storage) - : config(config), template_storage(template_storage), function_storage(function_storage) {} - - void render_to(std::ostream& os, const Template& tmpl, const json& data, json* loop_data = nullptr) { - output_stream = &os; - current_template = &tmpl; - data_input = &data; - if (loop_data) { - additional_data = *loop_data; - current_loop_data = &additional_data["loop"]; - } - - template_stack.emplace_back(current_template); - current_template->root.accept(*this); - - data_tmp_stack.clear(); - } -}; - -} // namespace inja - -#endif // INCLUDE_INJA_RENDERER_HPP_ - -// #include "template.hpp" - -// #include "utils.hpp" - - -namespace inja { - -/*! - * \brief Class for changing the configuration. - */ -class Environment { - std::string input_path; - std::string output_path; - - LexerConfig lexer_config; - ParserConfig parser_config; - RenderConfig render_config; - - FunctionStorage function_storage; - TemplateStorage template_storage; - -public: - Environment(): Environment("") {} - - explicit Environment(const std::string& global_path): input_path(global_path), output_path(global_path) {} - - Environment(const std::string& input_path, const std::string& output_path): input_path(input_path), output_path(output_path) {} - - /// Sets the opener and closer for template statements - void set_statement(const std::string& open, const std::string& close) { - lexer_config.statement_open = open; - lexer_config.statement_open_no_lstrip = open + "+"; - lexer_config.statement_open_force_lstrip = open + "-"; - lexer_config.statement_close = close; - lexer_config.statement_close_force_rstrip = "-" + close; - lexer_config.update_open_chars(); - } - - /// Sets the opener for template line statements - void set_line_statement(const std::string& open) { - lexer_config.line_statement = open; - lexer_config.update_open_chars(); - } - - /// Sets the opener and closer for template expressions - void set_expression(const std::string& open, const std::string& close) { - lexer_config.expression_open = open; - lexer_config.expression_open_force_lstrip = open + "-"; - lexer_config.expression_close = close; - lexer_config.expression_close_force_rstrip = "-" + close; - lexer_config.update_open_chars(); - } - - /// Sets the opener and closer for template comments - void set_comment(const std::string& open, const std::string& close) { - lexer_config.comment_open = open; - lexer_config.comment_open_force_lstrip = open + "-"; - lexer_config.comment_close = close; - lexer_config.comment_close_force_rstrip = "-" + close; - lexer_config.update_open_chars(); - } - - /// Sets whether to remove the first newline after a block - void set_trim_blocks(bool trim_blocks) { - lexer_config.trim_blocks = trim_blocks; - } - - /// Sets whether to strip the spaces and tabs from the start of a line to a block - void set_lstrip_blocks(bool lstrip_blocks) { - lexer_config.lstrip_blocks = lstrip_blocks; - } - - /// Sets the element notation syntax - void set_search_included_templates_in_files(bool search_in_files) { - parser_config.search_included_templates_in_files = search_in_files; - } - - /// Sets whether a missing include will throw an error - void set_throw_at_missing_includes(bool will_throw) { - render_config.throw_at_missing_includes = will_throw; - } - - Template parse(std::string_view input) { - Parser parser(parser_config, lexer_config, template_storage, function_storage); - return parser.parse(input); - } - - Template parse_template(const std::string& filename) { - Parser parser(parser_config, lexer_config, template_storage, function_storage); - auto result = Template(parser.load_file(input_path + static_cast(filename))); - parser.parse_into_template(result, input_path + static_cast(filename)); - return result; - } - - Template parse_file(const std::string& filename) { - return parse_template(filename); - } - - std::string render(std::string_view input, const json& data) { - return render(parse(input), data); - } - - std::string render(const Template& tmpl, const json& data) { - std::stringstream os; - render_to(os, tmpl, data); - return os.str(); - } - - std::string render_file(const std::string& filename, const json& data) { - return render(parse_template(filename), data); - } - - std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) { - const json data = load_json(filename_data); - return render_file(filename, data); - } - - void write(const std::string& filename, const json& data, const std::string& filename_out) { - std::ofstream file(output_path + filename_out); - file << render_file(filename, data); - file.close(); - } - - void write(const Template& temp, const json& data, const std::string& filename_out) { - std::ofstream file(output_path + filename_out); - file << render(temp, data); - file.close(); - } - - void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) { - const json data = load_json(filename_data); - write(filename, data, filename_out); - } - - void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) { - const json data = load_json(filename_data); - write(temp, data, filename_out); - } - - std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) { - Renderer(render_config, template_storage, function_storage).render_to(os, tmpl, data); - return os; - } - - std::string load_file(const std::string& filename) { - Parser parser(parser_config, lexer_config, template_storage, function_storage); - return parser.load_file(input_path + filename); - } - - json load_json(const std::string& filename) { - std::ifstream file; - file.open(input_path + filename); - if (file.fail()) { - INJA_THROW(FileError("failed accessing file at '" + input_path + filename + "'")); - } - - return json::parse(std::istreambuf_iterator(file), std::istreambuf_iterator()); - } - - /*! - @brief Adds a variadic callback - */ - void add_callback(const std::string& name, const CallbackFunction& callback) { - add_callback(name, -1, callback); - } - - /*! - @brief Adds a variadic void callback - */ - void add_void_callback(const std::string& name, const VoidCallbackFunction& callback) { - add_void_callback(name, -1, callback); - } - - /*! - @brief Adds a callback with given number or arguments - */ - void add_callback(const std::string& name, int num_args, const CallbackFunction& callback) { - function_storage.add_callback(name, num_args, callback); - } - - /*! - @brief Adds a void callback with given number or arguments - */ - void add_void_callback(const std::string& name, int num_args, const VoidCallbackFunction& callback) { - function_storage.add_callback(name, num_args, [callback](Arguments& args) { - callback(args); - return json(); - }); - } - - /** Includes a template with a given name into the environment. - * Then, a template can be rendered in another template using the - * include "" syntax. - */ - void include_template(const std::string& name, const Template& tmpl) { - template_storage[name] = tmpl; - } - - /*! - @brief Sets a function that is called when an included file is not found - */ - void set_include_callback(const std::function& callback) { - parser_config.include_callback = callback; - } -}; - -/*! -@brief render with default settings to a string -*/ -inline std::string render(std::string_view input, const json& data) { - return Environment().render(input, data); -} - -/*! -@brief render with default settings to the given output stream -*/ -inline void render_to(std::ostream& os, std::string_view input, const json& data) { - Environment env; - env.render_to(os, env.parse(input), data); -} - -} // namespace inja - -#endif // INCLUDE_INJA_ENVIRONMENT_HPP_ - -// #include "exceptions.hpp" - -// #include "parser.hpp" - -// #include "renderer.hpp" - -// #include "template.hpp" - - -#endif // INCLUDE_INJA_INJA_HPP_ diff --git a/ext/installfiles/linux/zerotier-containerized/Dockerfile b/ext/installfiles/linux/zerotier-containerized/Dockerfile index 7758faf3a..fd18eebb7 100644 --- a/ext/installfiles/linux/zerotier-containerized/Dockerfile +++ b/ext/installfiles/linux/zerotier-containerized/Dockerfile @@ -5,15 +5,19 @@ FROM debian:buster-slim as builder ## Supports x86_64, x86, arm, and arm64 RUN apt-get update && apt-get install -y curl gnupg -RUN apt-key adv --keyserver pgp.mit.edu --recv-keys 0x1657198823e52a61 && \ +RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys 0x1657198823e52a61 && \ echo "deb http://download.zerotier.com/debian/buster buster main" > /etc/apt/sources.list.d/zerotier.list -RUN apt-get update && apt-get install -y zerotier-one=1.8.6 -COPY ext/installfiles/linux/zerotier-containerized/main.sh /var/lib/zerotier-one/main.sh +RUN apt-get update && apt-get install -y zerotier-one=1.2.12 +RUN curl https://raw.githubusercontent.com/zerotier/ZeroTierOne/master/ext/installfiles/linux/zerotier-containerized/main.sh > /var/lib/zerotier-one/main.sh -FROM debian:buster-slim -LABEL version="1.8.6" +FROM alpine:latest +LABEL version="1.2.12" LABEL description="Containerized ZeroTier One for use on CoreOS or other Docker-only Linux hosts." +# Uncomment to build in container +# RUN apk add --update alpine-sdk linux-headers +RUN apk add --update libgcc libstdc++ + # ZeroTier relies on UDP port 9993 EXPOSE 9993/udp diff --git a/ext/installfiles/linux/zerotier-one.te b/ext/installfiles/linux/zerotier-one.te deleted file mode 100644 index 978df0b10..000000000 --- a/ext/installfiles/linux/zerotier-one.te +++ /dev/null @@ -1,14 +0,0 @@ - -module zerotier-one 1.0; - -require { - type unconfined_t; - type initrc_t; - class memprotect mmap_zero; -} - -#============= initrc_t ============== -allow initrc_t self:memprotect mmap_zero; - -#============= unconfined_t ============== -allow unconfined_t self:memprotect mmap_zero; diff --git a/ext/installfiles/mac/ZeroTier One.pkgproj b/ext/installfiles/mac/ZeroTier One.pkgproj index 70cedf9f9..7652041d4 100755 --- a/ext/installfiles/mac/ZeroTier One.pkgproj +++ b/ext/installfiles/mac/ZeroTier One.pkgproj @@ -2,715 +2,670 @@ - PACKAGES - - - MUST-CLOSE-APPLICATION-ITEMS - - MUST-CLOSE-APPLICATIONS - - PACKAGE_FILES - - DEFAULT_INSTALL_LOCATION - / - HIERARCHY - - CHILDREN - - - CHILDREN - - - CHILDREN - - GID - 80 - PATH - Utilities - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - BUNDLE_CAN_DOWNGRADE - - BUNDLE_POSTINSTALL_PATH - - PATH_TYPE - 0 - - BUNDLE_PREINSTALL_PATH - - PATH_TYPE - 0 - - CHILDREN - - GID - 80 - PATH - ../../../../DesktopUI/ZeroTier.app - PATH_TYPE - 1 - PERMISSIONS - 493 - TYPE - 3 - UID - 0 - - - GID - 80 - PATH - Applications - PATH_TYPE - 0 - PERMISSIONS - 509 - TYPE - 1 - UID - 0 - - - CHILDREN - - - CHILDREN - - - CHILDREN - - - CHILDREN - - - CHILDREN - - GID - 0 - PATH - get-proxy-settings.sh - PATH_TYPE - 1 - PERMISSIONS - 493 - TYPE - 3 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - launch.sh - PATH_TYPE - 1 - PERMISSIONS - 493 - TYPE - 3 - UID - 0 - - - CHILDREN - - GID - 80 - PATH - ../../../MacEthernetTapAgent - PATH_TYPE - 1 - PERMISSIONS - 493 - TYPE - 3 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - uninstall.sh - PATH_TYPE - 1 - PERMISSIONS - 493 - TYPE - 3 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - ../../../zerotier-one - PATH_TYPE - 1 - PERMISSIONS - 493 - TYPE - 3 - UID - 0 - - - GID - 80 - PATH - One - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 2 - UID - 0 - - - GID - 80 - PATH - ZeroTier - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 2 - UID - 0 - - - GID - 80 - PATH - Application Support - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Automator - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Documentation - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Filesystems - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Frameworks - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Input Methods - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Internet Plug-Ins - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - LaunchAgents - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - - CHILDREN - - GID - 0 - PATH - com.zerotier.one.plist - PATH_TYPE - 1 - PERMISSIONS - 420 - TYPE - 3 - UID - 0 - - - GID - 0 - PATH - LaunchDaemons - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - PreferencePanes - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Preferences - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 80 - PATH - Printers - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - PrivilegedHelperTools - PATH_TYPE - 0 - PERMISSIONS - 1005 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - QuickLook - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - QuickTime - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Screen Savers - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Scripts - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Services - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Widgets - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Extensions - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - GID - 0 - PATH - Library - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - - CHILDREN - - - CHILDREN - - GID - 0 - PATH - Extensions - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - GID - 0 - PATH - Library - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - GID - 0 - PATH - System - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - - CHILDREN - - GID - 0 - PATH - Shared - PATH_TYPE - 0 - PERMISSIONS - 1023 - TYPE - 1 - UID - 0 - - - GID - 80 - PATH - Users - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - GID - 0 - PATH - / - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - PAYLOAD_TYPE - 0 - PRESERVE_EXTENDED_ATTRIBUTES - - SHOW_INVISIBLE - - SPLIT_FORKS - - TREAT_MISSING_FILES_AS_WARNING - - VERSION - 5 - - PACKAGE_SCRIPTS - - POSTINSTALL_PATH - - PATH - postinst.sh - PATH_TYPE - 1 - - PREINSTALL_PATH - - PATH - preinst.sh - PATH_TYPE - 1 - - RESOURCES - - - PACKAGE_SETTINGS - - AUTHENTICATION - 1 - CONCLUSION_ACTION - 0 - FOLLOW_SYMBOLIC_LINKS - - IDENTIFIER - com.zerotier.pkg.ZeroTierOne - LOCATION - 0 - NAME - ZeroTier One - OVERWRITE_PERMISSIONS - - PAYLOAD_SIZE - -1 - REFERENCE_PATH - - RELOCATABLE - - USE_HFS+_COMPRESSION - - VERSION - 1.14.2 - - TYPE - 0 - UUID - 1B6AFC3A-9EA5-4401-83D4-37F06CD13CD6 - - PROJECT + PACKAGE_FILES + + DEFAULT_INSTALL_LOCATION + / + HIERARCHY + + CHILDREN + + + CHILDREN + + + CHILDREN + + GID + 80 + PATH + Utilities + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 80 + PATH + ../../../macui/build/Release/ZeroTier One.app + PATH_TYPE + 1 + PERMISSIONS + 493 + TYPE + 3 + UID + 0 + + + GID + 80 + PATH + Applications + PATH_TYPE + 0 + PERMISSIONS + 509 + TYPE + 1 + UID + 0 + + + CHILDREN + + + CHILDREN + + + CHILDREN + + + CHILDREN + + + CHILDREN + + GID + 0 + PATH + get-proxy-settings.sh + PATH_TYPE + 1 + PERMISSIONS + 493 + TYPE + 3 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + launch.sh + PATH_TYPE + 1 + PERMISSIONS + 493 + TYPE + 3 + UID + 0 + + + CHILDREN + + GID + 80 + PATH + ../../../MacEthernetTapAgent + PATH_TYPE + 1 + PERMISSIONS + 493 + TYPE + 3 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + uninstall.sh + PATH_TYPE + 1 + PERMISSIONS + 493 + TYPE + 3 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + ../../../zerotier-one + PATH_TYPE + 1 + PERMISSIONS + 493 + TYPE + 3 + UID + 0 + + + GID + 80 + PATH + One + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 2 + UID + 0 + + + GID + 80 + PATH + ZeroTier + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 2 + UID + 0 + + + GID + 80 + PATH + Application Support + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Automator + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Documentation + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Filesystems + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Frameworks + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Input Methods + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Internet Plug-Ins + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + LaunchAgents + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + + CHILDREN + + GID + 0 + PATH + com.zerotier.one.plist + PATH_TYPE + 1 + PERMISSIONS + 420 + TYPE + 3 + UID + 0 + + + GID + 0 + PATH + LaunchDaemons + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + PreferencePanes + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Preferences + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 80 + PATH + Printers + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + PrivilegedHelperTools + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + QuickLook + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + QuickTime + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Screen Savers + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Scripts + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Services + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Widgets + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + GID + 0 + PATH + Library + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + + CHILDREN + + + CHILDREN + + GID + 0 + PATH + Extensions + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + GID + 0 + PATH + Library + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + GID + 0 + PATH + System + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + + CHILDREN + + GID + 0 + PATH + Shared + PATH_TYPE + 0 + PERMISSIONS + 1023 + TYPE + 1 + UID + 0 + + + GID + 80 + PATH + Users + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + GID + 0 + PATH + / + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + PAYLOAD_TYPE + 0 + SHOW_INVISIBLE + + SPLIT_FORKS + + TREAT_MISSING_FILES_AS_WARNING + + VERSION + 3 + + PACKAGE_SCRIPTS + + POSTINSTALL_PATH + + PATH + postinst.sh + PATH_TYPE + 1 + + PREINSTALL_PATH + + PATH + preinst.sh + PATH_TYPE + 1 + + RESOURCES + + + PACKAGE_SETTINGS + + AUTHENTICATION + 1 + CONCLUSION_ACTION + 0 + FOLLOW_SYMBOLIC_LINKS + + IDENTIFIER + com.zerotier.pkg.ZeroTierOne + LOCATION + 0 + NAME + + OVERWRITE_PERMISSIONS + + PAYLOAD_SIZE + -1 + RELOCATABLE + + USE_HFS+_COMPRESSION + + VERSION + 1.4.0 + PROJECT_COMMENTS NOTES @@ -746,183 +701,8 @@ dG1sPgo= - PROJECT_PRESENTATION - - BACKGROUND - - APPAREANCES - - DARK_AQUA - - LIGHT_AQUA - - - SHARED_SETTINGS_FOR_ALL_APPAREANCES - - - INSTALLATION TYPE - - HIERARCHIES - - INSTALLER - - LIST - - - CHILDREN - - DESCRIPTION - - OPTIONS - - HIDDEN - - STATE - 1 - - PACKAGE_UUID - 1B6AFC3A-9EA5-4401-83D4-37F06CD13CD6 - TITLE - - TYPE - 0 - UUID - 0FC7D07B-9BA9-4711-BB08-A05099C9E2B9 - - - REMOVED - - - - MODE - 0 - - INSTALLATION_STEPS - - - ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS - ICPresentationViewIntroductionController - INSTALLER_PLUGIN - Introduction - LIST_TITLE_KEY - InstallerSectionTitle - - - ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS - ICPresentationViewReadMeController - INSTALLER_PLUGIN - ReadMe - LIST_TITLE_KEY - InstallerSectionTitle - - - ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS - ICPresentationViewLicenseController - INSTALLER_PLUGIN - License - LIST_TITLE_KEY - InstallerSectionTitle - - - ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS - ICPresentationViewDestinationSelectController - INSTALLER_PLUGIN - TargetSelect - LIST_TITLE_KEY - InstallerSectionTitle - - - ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS - ICPresentationViewInstallationTypeController - INSTALLER_PLUGIN - PackageSelection - LIST_TITLE_KEY - InstallerSectionTitle - - - ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS - ICPresentationViewInstallationController - INSTALLER_PLUGIN - Install - LIST_TITLE_KEY - InstallerSectionTitle - - - ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS - ICPresentationViewSummaryController - INSTALLER_PLUGIN - Summary - LIST_TITLE_KEY - InstallerSectionTitle - - - INTRODUCTION - - LOCALIZATIONS - - - LICENSE - - LOCALIZATIONS - - MODE - 0 - - README - - LOCALIZATIONS - - - TITLE - - LOCALIZATIONS - - - - PROJECT_REQUIREMENTS - - LIST - - - BEHAVIOR - 3 - DICTIONARY - - IC_REQUIREMENT_OS_DISK_TYPE - 0 - IC_REQUIREMENT_OS_DISTRIBUTION_TYPE - 0 - IC_REQUIREMENT_OS_MINIMUM_VERSION - 101300 - - IC_REQUIREMENT_CHECK_TYPE - 1 - IDENTIFIER - fr.whitebox.Packages.requirement.os - MESSAGE - - NAME - Operating System - STATE - - - - RESOURCES - - ROOT_VOLUME_ONLY - - PROJECT_SETTINGS - ADVANCED_OPTIONS - - installer-script.options:hostArchitectures - - x86_64,arm64 - - - BUILD_FORMAT - 0 BUILD_PATH PATH @@ -1102,17 +882,10 @@ ZeroTier One PAYLOAD_ONLY - TREAT_MISSING_PRESENTATION_DOCUMENTS_AS_WARNING - - SHARED_GLOBAL_DATA - - IC_REQUIREMENT_JAVASCRIPT_SHARED_SOURCE_CODE - - TYPE - 0 + 1 VERSION 2 diff --git a/ext/installfiles/mac/launch.sh b/ext/installfiles/mac/launch.sh index ab274cbfb..b02a66708 100755 --- a/ext/installfiles/mac/launch.sh +++ b/ext/installfiles/mac/launch.sh @@ -1,4 +1,3 @@ #!/bin/bash export PATH="/Library/Application Support/ZeroTier/One:/bin:/usr/bin:/sbin:/usr/sbin" -/usr/bin/killall MacEthernetTapAgent >>/dev/null 2>&1 exec zerotier-one diff --git a/ext/installfiles/mac/postinst.sh b/ext/installfiles/mac/postinst.sh index 707b46701..b4ea2ee23 100755 --- a/ext/installfiles/mac/postinst.sh +++ b/ext/installfiles/mac/postinst.sh @@ -2,8 +2,31 @@ export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin +OSX_RELEASE=`sw_vers -productVersion | cut -d . -f 1,2` + +launchctl unload /Library/LaunchDaemons/com.zerotier.one.plist >>/dev/null 2>&1 +sleep 0.5 + cd "/Library/Application Support/ZeroTier/One" +if [ "$OSX_RELEASE" = "10.7" ]; then + # OSX 10.7 cannot use the new tap driver since the new way of kext signing + # is not backward compatible. Pull the old one for 10.7 users and replace. + # We use https to fetch and check hash as an extra added measure. + rm -f tap.kext.10_7.tar.gz + curl -s https://download.zerotier.com/tap.kext.10_7.tar.gz >tap.kext.10_7.tar.gz + if [ -s tap.kext.10_7.tar.gz -a "`shasum -a 256 tap.kext.10_7.tar.gz | cut -d ' ' -f 1`" = "e133d4832cef571621d3618f417381b44f51a76ed625089fb4e545e65d3ef2a9" ]; then + rm -rf tap.kext + tar -xzf tap.kext.10_7.tar.gz + fi + rm -f tap.kext.10_7.tar.gz +fi + +rm -rf node.log node.log.old root-topology shutdownIfUnreadable autoupdate.log updates.d ui peers.save + +chown -R 0 tap.kext +chgrp -R 0 tap.kext + if [ ! -f authtoken.secret ]; then head -c 1024 /dev/urandom | md5 | head -c 24 >authtoken.secret chown 0 authtoken.secret @@ -11,39 +34,26 @@ if [ ! -f authtoken.secret ]; then chmod 0600 authtoken.secret fi -if [ -f zerotier-one.pid ]; then - kill `cat zerotier-one.pid` - sleep 1 - killall MacEthernetTapAgent - sleep 1 - killall -9 MacEthernetTapAgent - sleep 1 - if [ -f zerotier-one.pid ]; then - kill -9 `cat zerotier-one.pid` - rm -f zerotier-one.pid - fi -fi -launchctl load /Library/LaunchDaemons/com.zerotier.one.plist >>/dev/null 2>&1 -sleep 1 - rm -f zerotier-cli zerotier-idtool ln -sf zerotier-one zerotier-cli ln -sf zerotier-one zerotier-idtool -if [ ! -d /usr/local/bin ]; then - mkdir -p /usr/local/bin -fi +mkdir -p /usr/local/bin cd /usr/local/bin rm -f zerotier-cli zerotier-idtool ln -sf "/Library/Application Support/ZeroTier/One/zerotier-one" zerotier-cli ln -sf "/Library/Application Support/ZeroTier/One/zerotier-one" zerotier-idtool +cd "/Library/Application Support/ZeroTier/One" +kextload -r . tap.kext >>/dev/null 2>&1 & +disown %1 + +launchctl load /Library/LaunchDaemons/com.zerotier.one.plist >>/dev/null 2>&1 + +sleep 1 + if [ -f /tmp/zt1-gui-restart.tmp ]; then for u in `cat /tmp/zt1-gui-restart.tmp`; do - if [ -f '/Applications/ZeroTier One.app/Contents/MacOS/ZeroTier One' ]; then - su $u -c '/usr/bin/open /Applications/ZeroTier\ One.app &' >>/dev/null 2>&1 & - else - su $u -c '/usr/bin/open /Applications/ZeroTier.app &' >>/dev/null 2>&1 & - fi + su $u -c '/Applications/ZeroTier\ One.app/Contents/MacOS/ZeroTier\ One &' >>/dev/null 2>&1 & done fi rm -f /tmp/zt1-gui-restart.tmp diff --git a/ext/installfiles/mac/preinst.sh b/ext/installfiles/mac/preinst.sh index d2df6a3a8..af2a93208 100755 --- a/ext/installfiles/mac/preinst.sh +++ b/ext/installfiles/mac/preinst.sh @@ -8,32 +8,32 @@ for i in `ps axuwww | tr -s ' ' ',' | grep -F '/Applications/ZeroTier,One.app' | p=`echo $i | cut -d , -f 2` if [ ! -z "$u" -a "0$p" -gt 0 ]; then kill $p >>/dev/null 2>&1 - sleep 0.5 - kill -9 $p >>/dev/null 2>&1 - echo "$u" >>/tmp/zt1-gui-restart.tmp - fi -done -for i in `ps axuwww | tr -s ' ' ',' | grep -F '/Applications/ZeroTier.app' | grep -F -v grep | cut -d , -f 1,2 | xargs`; do - u=`echo $i | cut -d , -f 1` - p=`echo $i | cut -d , -f 2` - if [ ! -z "$u" -a "0$p" -gt 0 ]; then - kill $p >>/dev/null 2>&1 - sleep 0.5 + sleep 0.2 kill -9 $p >>/dev/null 2>&1 echo "$u" >>/tmp/zt1-gui-restart.tmp fi done chmod 0600 /tmp/zt1-gui-restart.tmp -cd "/Applications" -rm -rf "ZeroTier One.app" -rm -rf "ZeroTier.app" - -if [ -d '/Library/Application Support/ZeroTier/One' ]; then - cd '/Library/Application Support/ZeroTier/One' - # ensure that file locking doesn't cause issues with replacing the binary - rm -f zerotier-one - rm -f MacEthernetTapAgent +if [ -f /Library/LaunchDaemons/com.zerotier.one.plist ]; then + launchctl unload /Library/LaunchDaemons/com.zerotier.one.plist >>/dev/null 2>&1 fi +sleep 1 + +if [ -d "/Library/Application Support/ZeroTier/One" ]; then + cd "/Library/Application Support/ZeroTier/One" + if [ -f "zerotier-one.pid" ]; then + ztpid=`cat zerotier-one.pid` + if [ "$ztpid" -gt "0" ]; then + kill `cat zerotier-one.pid` + fi + fi +fi + +sleep 1 + +cd "/Applications" +rm -rf "ZeroTier One.app" + exit 0 diff --git a/ext/installfiles/mac/uninstall.sh b/ext/installfiles/mac/uninstall.sh index 4b6abe13a..52c09b630 100755 --- a/ext/installfiles/mac/uninstall.sh +++ b/ext/installfiles/mac/uninstall.sh @@ -21,10 +21,12 @@ killall -TERM zerotier-one >>/dev/null 2>&1 sleep 1 killall -KILL zerotier-one >>/dev/null 2>&1 +echo "Making sure kext is unloaded..." +kextunload '/Library/Application Support/ZeroTier/One/tap.kext' >>/dev/null 2>&1 + echo "Removing ZeroTier One files..." rm -rf '/Applications/ZeroTier One.app' -rm -rf '/Applications/ZeroTier.app' rm -f '/usr/local/bin/zerotier-one' '/usr/local/bin/zerotier-idtool' '/usr/local/bin/zerotier-cli' '/Library/LaunchDaemons/com.zerotier.one.plist' cd '/Library/Application Support/ZeroTier/One' diff --git a/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x64).aip b/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x64).aip index 3c3d6a417..7ff1a05e7 100644 --- a/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x64).aip +++ b/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x64).aip @@ -1,187 +1,184 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x86).aip b/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x86).aip index 50d5f5575..ef3d58cc7 100644 --- a/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x86).aip +++ b/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x86).aip @@ -1,188 +1,185 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/installfiles/windows/ZeroTier One.aip b/ext/installfiles/windows/ZeroTier One.aip index 43b3223f5..26b529d7d 100644 --- a/ext/installfiles/windows/ZeroTier One.aip +++ b/ext/installfiles/windows/ZeroTier One.aip @@ -1,15 +1,17 @@ - + - + + - + - + + @@ -22,115 +24,124 @@ + - + - + + - + - - - - - - + + + + + - + - + + - - - - - - - - + + + + + + - - + + - - + + - - - - - - + + - - + - - - - + + + - - - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + + + + + + + + - - + + + + + + + + + + - + - - - - + + + + @@ -139,7 +150,6 @@ - @@ -147,6 +157,11 @@ + + + + + @@ -175,43 +190,23 @@ + + + - + - - - - - - - - - - - - - - - - - - - - - - - - + @@ -229,48 +224,50 @@ - - - - + + + + + - - - - - - - - + + + + + + + + - - + + + - + @@ -280,13 +277,14 @@ + - + - + @@ -299,43 +297,24 @@ - - + - - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - + + + + + @@ -343,24 +322,26 @@ - - - + + + - - - - + + + + + + - + @@ -368,95 +349,67 @@ - - - - - - - - - - - + + + + + + + + + + + + - - + + - + - - - + + + - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - + + @@ -467,8 +420,7 @@ - - + @@ -477,6 +429,10 @@ + + + + @@ -498,28 +454,28 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/installfiles/windows/ZeroTier One.back.aip b/ext/installfiles/windows/ZeroTier One.back.aip deleted file mode 100644 index 9ad818277..000000000 --- a/ext/installfiles/windows/ZeroTier One.back.aip +++ /dev/null @@ -1,558 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ext/nlohmann/LICENSE.MIT b/ext/json/LICENSE.MIT similarity index 100% rename from ext/nlohmann/LICENSE.MIT rename to ext/json/LICENSE.MIT diff --git a/ext/nlohmann/README.md b/ext/json/README.md similarity index 100% rename from ext/nlohmann/README.md rename to ext/json/README.md diff --git a/ext/nlohmann/json.hpp b/ext/json/json.hpp similarity index 51% rename from ext/nlohmann/json.hpp rename to ext/json/json.hpp index 8959265da..b80386f3f 100644 --- a/ext/nlohmann/json.hpp +++ b/ext/json/json.hpp @@ -1,12 +1,12 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.10.2 +| | |__ | | | | | | version 3.2.0 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . SPDX-License-Identifier: MIT -Copyright (c) 2013-2019 Niels Lohmann . +Copyright (c) 2013-2018 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,2194 +27,94 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef INCLUDE_NLOHMANN_JSON_HPP_ -#define INCLUDE_NLOHMANN_JSON_HPP_ +#ifndef NLOHMANN_JSON_HPP +#define NLOHMANN_JSON_HPP #define NLOHMANN_JSON_VERSION_MAJOR 3 -#define NLOHMANN_JSON_VERSION_MINOR 10 -#define NLOHMANN_JSON_VERSION_PATCH 2 +#define NLOHMANN_JSON_VERSION_MINOR 2 +#define NLOHMANN_JSON_VERSION_PATCH 0 #include // all_of, find, for_each +#include // assert +#include // and, not, or #include // nullptr_t, ptrdiff_t, size_t #include // hash, less #include // initializer_list -#ifndef JSON_NO_IO - #include // istream, ostream -#endif // JSON_NO_IO -#include // random_access_iterator_tag -#include // unique_ptr +#include // istream, ostream +#include // iterator_traits, random_access_iterator_tag #include // accumulate #include // string, stoi, to_string #include // declval, forward, move, pair, swap -#include // vector -// #include +// #include +#ifndef NLOHMANN_JSON_FWD_HPP +#define NLOHMANN_JSON_FWD_HPP - -#include -#include - -// #include - - -#include // transform -#include // array -#include // forward_list -#include // inserter, front_inserter, end +#include // int64_t, uint64_t #include // map +#include // allocator #include // string -#include // tuple, make_tuple -#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible -#include // unordered_map -#include // pair, declval -#include // valarray - -// #include - - -#include // exception -#include // runtime_error -#include // to_string #include // vector -// #include - - -#include // array -#include // size_t -#include // uint8_t -#include // string - +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ namespace nlohmann { -namespace detail -{ -/////////////////////////// -// JSON type enumeration // -/////////////////////////// +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer> +class basic_json; /*! -@brief the JSON type enumeration +@brief JSON Pointer -This enumeration collects the different JSON types. It is internally used to -distinguish the stored values, and the functions @ref basic_json::is_null(), -@ref basic_json::is_object(), @ref basic_json::is_array(), -@ref basic_json::is_string(), @ref basic_json::is_boolean(), -@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), -@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), -@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and -@ref basic_json::is_structured() rely on it. +A JSON pointer defines a string syntax for identifying a specific value +within a JSON document. It can be used with functions `at` and +`operator[]`. Furthermore, JSON pointers are the base for JSON patches. -@note There are three enumeration entries (number_integer, number_unsigned, and -number_float), because the library distinguishes these three types for numbers: -@ref basic_json::number_unsigned_t is used for unsigned integers, -@ref basic_json::number_integer_t is used for signed integers, and -@ref basic_json::number_float_t is used for floating-point numbers or to -approximate integers which do not fit in the limits of their respective type. +@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) -@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON -value with the default value for a given type +@since version 2.0.0 +*/ +template +class json_pointer; + +/*! +@brief default JSON class + +This type is the default specialization of the @ref basic_json class which +uses the standard template types. @since version 1.0.0 */ -enum class value_t : std::uint8_t -{ - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (signed integer) - number_unsigned, ///< number value (unsigned integer) - number_float, ///< number value (floating-point) - binary, ///< binary array (ordered collection of bytes) - discarded ///< discarded by the parser callback function -}; - -/*! -@brief comparison operator for JSON types - -Returns an ordering that is similar to Python: -- order: null < boolean < number < object < array < string < binary -- furthermore, each type is not smaller than itself -- discarded values are not comparable -- binary is represented as a b"" string in python and directly comparable to a - string; however, making a binary array directly comparable with a string would - be surprising behavior in a JSON file. - -@since version 1.0.0 -*/ -inline bool operator<(const value_t lhs, const value_t rhs) noexcept -{ - static constexpr std::array order = {{ - 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, - 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, - 6 /* binary */ - } - }; - - const auto l_index = static_cast(lhs); - const auto r_index = static_cast(rhs); - return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +using json = basic_json<>; } -} // namespace detail -} // namespace nlohmann -// #include +#endif - -#include // #include -#include // pair -// #include - - -/* Hedley - https://nemequ.github.io/hedley - * Created by Evan Nemerson - * - * To the extent possible under law, the author(s) have dedicated all - * copyright and related and neighboring rights to this software to - * the public domain worldwide. This software is distributed without - * any warranty. - * - * For details, see . - * SPDX-License-Identifier: CC0-1.0 - */ - -#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) -#if defined(JSON_HEDLEY_VERSION) - #undef JSON_HEDLEY_VERSION -#endif -#define JSON_HEDLEY_VERSION 15 - -#if defined(JSON_HEDLEY_STRINGIFY_EX) - #undef JSON_HEDLEY_STRINGIFY_EX -#endif -#define JSON_HEDLEY_STRINGIFY_EX(x) #x - -#if defined(JSON_HEDLEY_STRINGIFY) - #undef JSON_HEDLEY_STRINGIFY -#endif -#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) - -#if defined(JSON_HEDLEY_CONCAT_EX) - #undef JSON_HEDLEY_CONCAT_EX -#endif -#define JSON_HEDLEY_CONCAT_EX(a,b) a##b - -#if defined(JSON_HEDLEY_CONCAT) - #undef JSON_HEDLEY_CONCAT -#endif -#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) - -#if defined(JSON_HEDLEY_CONCAT3_EX) - #undef JSON_HEDLEY_CONCAT3_EX -#endif -#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c - -#if defined(JSON_HEDLEY_CONCAT3) - #undef JSON_HEDLEY_CONCAT3 -#endif -#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) - -#if defined(JSON_HEDLEY_VERSION_ENCODE) - #undef JSON_HEDLEY_VERSION_ENCODE -#endif -#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) - -#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) - #undef JSON_HEDLEY_VERSION_DECODE_MAJOR -#endif -#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) - -#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) - #undef JSON_HEDLEY_VERSION_DECODE_MINOR -#endif -#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) - -#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) - #undef JSON_HEDLEY_VERSION_DECODE_REVISION -#endif -#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) - -#if defined(JSON_HEDLEY_GNUC_VERSION) - #undef JSON_HEDLEY_GNUC_VERSION -#endif -#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) - #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) -#elif defined(__GNUC__) - #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) -#endif - -#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) - #undef JSON_HEDLEY_GNUC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_GNUC_VERSION) - #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_MSVC_VERSION) - #undef JSON_HEDLEY_MSVC_VERSION -#endif -#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) - #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) -#elif defined(_MSC_FULL_VER) && !defined(__ICL) - #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) -#elif defined(_MSC_VER) && !defined(__ICL) - #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) -#endif - -#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) - #undef JSON_HEDLEY_MSVC_VERSION_CHECK -#endif -#if !defined(JSON_HEDLEY_MSVC_VERSION) - #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) -#elif defined(_MSC_VER) && (_MSC_VER >= 1400) - #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) -#elif defined(_MSC_VER) && (_MSC_VER >= 1200) - #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) -#else - #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) -#endif - -#if defined(JSON_HEDLEY_INTEL_VERSION) - #undef JSON_HEDLEY_INTEL_VERSION -#endif -#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) - #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) -#elif defined(__INTEL_COMPILER) && !defined(__ICL) - #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) -#endif - -#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) - #undef JSON_HEDLEY_INTEL_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_INTEL_VERSION) - #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_INTEL_CL_VERSION) - #undef JSON_HEDLEY_INTEL_CL_VERSION -#endif -#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) - #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) -#endif - -#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) - #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_INTEL_CL_VERSION) - #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_PGI_VERSION) - #undef JSON_HEDLEY_PGI_VERSION -#endif -#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) - #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) -#endif - -#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) - #undef JSON_HEDLEY_PGI_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_PGI_VERSION) - #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_SUNPRO_VERSION) - #undef JSON_HEDLEY_SUNPRO_VERSION -#endif -#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) - #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) -#elif defined(__SUNPRO_C) - #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) -#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) - #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) -#elif defined(__SUNPRO_CC) - #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) -#endif - -#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) - #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_SUNPRO_VERSION) - #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) - #undef JSON_HEDLEY_EMSCRIPTEN_VERSION -#endif -#if defined(__EMSCRIPTEN__) - #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) -#endif - -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) - #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) - #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_ARM_VERSION) - #undef JSON_HEDLEY_ARM_VERSION -#endif -#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) - #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) -#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) - #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) -#endif - -#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) - #undef JSON_HEDLEY_ARM_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_ARM_VERSION) - #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_IBM_VERSION) - #undef JSON_HEDLEY_IBM_VERSION -#endif -#if defined(__ibmxl__) - #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) -#elif defined(__xlC__) && defined(__xlC_ver__) - #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) -#elif defined(__xlC__) - #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) -#endif - -#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) - #undef JSON_HEDLEY_IBM_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_IBM_VERSION) - #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_VERSION) - #undef JSON_HEDLEY_TI_VERSION -#endif -#if \ - defined(__TI_COMPILER_VERSION__) && \ - ( \ - defined(__TMS470__) || defined(__TI_ARM__) || \ - defined(__MSP430__) || \ - defined(__TMS320C2000__) \ - ) -#if (__TI_COMPILER_VERSION__ >= 16000000) - #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif -#endif - -#if defined(JSON_HEDLEY_TI_VERSION_CHECK) - #undef JSON_HEDLEY_TI_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_VERSION) - #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL2000_VERSION) - #undef JSON_HEDLEY_TI_CL2000_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) - #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL2000_VERSION) - #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL430_VERSION) - #undef JSON_HEDLEY_TI_CL430_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) - #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL430_VERSION) - #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) - #undef JSON_HEDLEY_TI_ARMCL_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) - #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) - #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) - #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL6X_VERSION) - #undef JSON_HEDLEY_TI_CL6X_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) - #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL6X_VERSION) - #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL7X_VERSION) - #undef JSON_HEDLEY_TI_CL7X_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) - #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL7X_VERSION) - #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) - #undef JSON_HEDLEY_TI_CLPRU_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) - #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) - #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_CRAY_VERSION) - #undef JSON_HEDLEY_CRAY_VERSION -#endif -#if defined(_CRAYC) - #if defined(_RELEASE_PATCHLEVEL) - #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) - #else - #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) - #endif -#endif - -#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) - #undef JSON_HEDLEY_CRAY_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_CRAY_VERSION) - #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_IAR_VERSION) - #undef JSON_HEDLEY_IAR_VERSION -#endif -#if defined(__IAR_SYSTEMS_ICC__) - #if __VER__ > 1000 - #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) - #else - #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) - #endif -#endif - -#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) - #undef JSON_HEDLEY_IAR_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_IAR_VERSION) - #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TINYC_VERSION) - #undef JSON_HEDLEY_TINYC_VERSION -#endif -#if defined(__TINYC__) - #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) -#endif - -#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) - #undef JSON_HEDLEY_TINYC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TINYC_VERSION) - #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_DMC_VERSION) - #undef JSON_HEDLEY_DMC_VERSION -#endif -#if defined(__DMC__) - #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) -#endif - -#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) - #undef JSON_HEDLEY_DMC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_DMC_VERSION) - #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_COMPCERT_VERSION) - #undef JSON_HEDLEY_COMPCERT_VERSION -#endif -#if defined(__COMPCERT_VERSION__) - #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) -#endif - -#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) - #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_COMPCERT_VERSION) - #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_PELLES_VERSION) - #undef JSON_HEDLEY_PELLES_VERSION -#endif -#if defined(__POCC__) - #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) -#endif - -#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) - #undef JSON_HEDLEY_PELLES_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_PELLES_VERSION) - #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_MCST_LCC_VERSION) - #undef JSON_HEDLEY_MCST_LCC_VERSION -#endif -#if defined(__LCC__) && defined(__LCC_MINOR__) - #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) -#endif - -#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) - #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_MCST_LCC_VERSION) - #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_GCC_VERSION) - #undef JSON_HEDLEY_GCC_VERSION -#endif -#if \ - defined(JSON_HEDLEY_GNUC_VERSION) && \ - !defined(__clang__) && \ - !defined(JSON_HEDLEY_INTEL_VERSION) && \ - !defined(JSON_HEDLEY_PGI_VERSION) && \ - !defined(JSON_HEDLEY_ARM_VERSION) && \ - !defined(JSON_HEDLEY_CRAY_VERSION) && \ - !defined(JSON_HEDLEY_TI_VERSION) && \ - !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ - !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ - !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ - !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ - !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ - !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ - !defined(__COMPCERT__) && \ - !defined(JSON_HEDLEY_MCST_LCC_VERSION) - #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION -#endif - -#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) - #undef JSON_HEDLEY_GCC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_GCC_VERSION) - #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) - #undef JSON_HEDLEY_HAS_ATTRIBUTE -#endif -#if \ - defined(__has_attribute) && \ - ( \ - (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ - ) -# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) -#else -# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) - #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE -#endif -#if defined(__has_attribute) - #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) -#else - #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) - #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE -#endif -#if defined(__has_attribute) - #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) -#else - #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) - #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE -#endif -#if \ - defined(__has_cpp_attribute) && \ - defined(__cplusplus) && \ - (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) -#else - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) - #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS -#endif -#if !defined(__cplusplus) || !defined(__has_cpp_attribute) - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) -#elif \ - !defined(JSON_HEDLEY_PGI_VERSION) && \ - !defined(JSON_HEDLEY_IAR_VERSION) && \ - (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ - (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) -#else - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) - #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE -#endif -#if defined(__has_cpp_attribute) && defined(__cplusplus) - #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) -#else - #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) - #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE -#endif -#if defined(__has_cpp_attribute) && defined(__cplusplus) - #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) -#else - #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_BUILTIN) - #undef JSON_HEDLEY_HAS_BUILTIN -#endif -#if defined(__has_builtin) - #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) -#else - #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) - #undef JSON_HEDLEY_GNUC_HAS_BUILTIN -#endif -#if defined(__has_builtin) - #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) -#else - #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) - #undef JSON_HEDLEY_GCC_HAS_BUILTIN -#endif -#if defined(__has_builtin) - #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) -#else - #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_FEATURE) - #undef JSON_HEDLEY_HAS_FEATURE -#endif -#if defined(__has_feature) - #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) -#else - #define JSON_HEDLEY_HAS_FEATURE(feature) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) - #undef JSON_HEDLEY_GNUC_HAS_FEATURE -#endif -#if defined(__has_feature) - #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) -#else - #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) - #undef JSON_HEDLEY_GCC_HAS_FEATURE -#endif -#if defined(__has_feature) - #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) -#else - #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_EXTENSION) - #undef JSON_HEDLEY_HAS_EXTENSION -#endif -#if defined(__has_extension) - #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) -#else - #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) - #undef JSON_HEDLEY_GNUC_HAS_EXTENSION -#endif -#if defined(__has_extension) - #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) -#else - #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) - #undef JSON_HEDLEY_GCC_HAS_EXTENSION -#endif -#if defined(__has_extension) - #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) -#else - #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) - #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) - #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) -#else - #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) - #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) - #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) -#else - #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) - #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) - #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) -#else - #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_WARNING) - #undef JSON_HEDLEY_HAS_WARNING -#endif -#if defined(__has_warning) - #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) -#else - #define JSON_HEDLEY_HAS_WARNING(warning) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) - #undef JSON_HEDLEY_GNUC_HAS_WARNING -#endif -#if defined(__has_warning) - #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) -#else - #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_WARNING) - #undef JSON_HEDLEY_GCC_HAS_WARNING -#endif -#if defined(__has_warning) - #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) -#else - #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if \ - (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ - defined(__clang__) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) - #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) - #define JSON_HEDLEY_PRAGMA(value) __pragma(value) -#else - #define JSON_HEDLEY_PRAGMA(value) -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) - #undef JSON_HEDLEY_DIAGNOSTIC_PUSH -#endif -#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) - #undef JSON_HEDLEY_DIAGNOSTIC_POP -#endif -#if defined(__clang__) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) - #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) -#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") -#elif \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") -#else - #define JSON_HEDLEY_DIAGNOSTIC_PUSH - #define JSON_HEDLEY_DIAGNOSTIC_POP -#endif - -/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for - HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ -#endif -#if defined(__cplusplus) -# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") -# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") -# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") -# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ - _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ - _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ - xpr \ - JSON_HEDLEY_DIAGNOSTIC_POP -# else -# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ - _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ - xpr \ - JSON_HEDLEY_DIAGNOSTIC_POP -# endif -# else -# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ - xpr \ - JSON_HEDLEY_DIAGNOSTIC_POP -# endif -# endif -#endif -#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x -#endif - -#if defined(JSON_HEDLEY_CONST_CAST) - #undef JSON_HEDLEY_CONST_CAST -#endif -#if defined(__cplusplus) -# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) -#elif \ - JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ - ((T) (expr)); \ - JSON_HEDLEY_DIAGNOSTIC_POP \ - })) -#else -# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_REINTERPRET_CAST) - #undef JSON_HEDLEY_REINTERPRET_CAST -#endif -#if defined(__cplusplus) - #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) -#else - #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_STATIC_CAST) - #undef JSON_HEDLEY_STATIC_CAST -#endif -#if defined(__cplusplus) - #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) -#else - #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_CPP_CAST) - #undef JSON_HEDLEY_CPP_CAST -#endif -#if defined(__cplusplus) -# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") -# define JSON_HEDLEY_CPP_CAST(T, expr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ - ((T) (expr)) \ - JSON_HEDLEY_DIAGNOSTIC_POP -# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) -# define JSON_HEDLEY_CPP_CAST(T, expr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("diag_suppress=Pe137") \ - JSON_HEDLEY_DIAGNOSTIC_POP -# else -# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) -# endif -#else -# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") -#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") -#elif \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") -#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) -#elif \ - JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") -#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") -#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") -#elif \ - JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION -#endif - -#if defined(JSON_HEDLEY_DEPRECATED) - #undef JSON_HEDLEY_DEPRECATED -#endif -#if defined(JSON_HEDLEY_DEPRECATED_FOR) - #undef JSON_HEDLEY_DEPRECATED_FOR -#endif -#if \ - JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) -#elif \ - (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) -#elif defined(__cplusplus) && (__cplusplus >= 201402L) - #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) -#elif \ - JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) - #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ - JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") -#else - #define JSON_HEDLEY_DEPRECATED(since) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) -#endif - -#if defined(JSON_HEDLEY_UNAVAILABLE) - #undef JSON_HEDLEY_UNAVAILABLE -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) -#else - #define JSON_HEDLEY_UNAVAILABLE(available_since) -#endif - -#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) - #undef JSON_HEDLEY_WARN_UNUSED_RESULT -#endif -#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) - #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) -#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) - #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) -#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) - #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) -#elif defined(_Check_return_) /* SAL */ - #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ -#else - #define JSON_HEDLEY_WARN_UNUSED_RESULT - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) -#endif - -#if defined(JSON_HEDLEY_SENTINEL) - #undef JSON_HEDLEY_SENTINEL -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) -#else - #define JSON_HEDLEY_SENTINEL(position) -#endif - -#if defined(JSON_HEDLEY_NO_RETURN) - #undef JSON_HEDLEY_NO_RETURN -#endif -#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_NO_RETURN __noreturn -#elif \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) -#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L - #define JSON_HEDLEY_NO_RETURN _Noreturn -#elif defined(__cplusplus) && (__cplusplus >= 201103L) - #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) -#elif \ - JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) - #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) - #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) -#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) - #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") -#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) - #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) - #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) -#else - #define JSON_HEDLEY_NO_RETURN -#endif - -#if defined(JSON_HEDLEY_NO_ESCAPE) - #undef JSON_HEDLEY_NO_ESCAPE -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) - #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) -#else - #define JSON_HEDLEY_NO_ESCAPE -#endif - -#if defined(JSON_HEDLEY_UNREACHABLE) - #undef JSON_HEDLEY_UNREACHABLE -#endif -#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) - #undef JSON_HEDLEY_UNREACHABLE_RETURN -#endif -#if defined(JSON_HEDLEY_ASSUME) - #undef JSON_HEDLEY_ASSUME -#endif -#if \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_ASSUME(expr) __assume(expr) -#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) - #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) -#elif \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) - #if defined(__cplusplus) - #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) - #else - #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) - #endif -#endif -#if \ - (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() -#elif defined(JSON_HEDLEY_ASSUME) - #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) -#endif -#if !defined(JSON_HEDLEY_ASSUME) - #if defined(JSON_HEDLEY_UNREACHABLE) - #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) - #else - #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) - #endif -#endif -#if defined(JSON_HEDLEY_UNREACHABLE) - #if \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) - #else - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() - #endif -#else - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) -#endif -#if !defined(JSON_HEDLEY_UNREACHABLE) - #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) -#endif - -JSON_HEDLEY_DIAGNOSTIC_PUSH -#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") - #pragma clang diagnostic ignored "-Wpedantic" -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) - #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -#endif -#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) - #if defined(__clang__) - #pragma clang diagnostic ignored "-Wvariadic-macros" - #elif defined(JSON_HEDLEY_GCC_VERSION) - #pragma GCC diagnostic ignored "-Wvariadic-macros" - #endif -#endif -#if defined(JSON_HEDLEY_NON_NULL) - #undef JSON_HEDLEY_NON_NULL -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) - #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) -#else - #define JSON_HEDLEY_NON_NULL(...) -#endif -JSON_HEDLEY_DIAGNOSTIC_POP - -#if defined(JSON_HEDLEY_PRINTF_FORMAT) - #undef JSON_HEDLEY_PRINTF_FORMAT -#endif -#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) -#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) -#elif \ - JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) -#else - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) -#endif - -#if defined(JSON_HEDLEY_CONSTEXPR) - #undef JSON_HEDLEY_CONSTEXPR -#endif -#if defined(__cplusplus) - #if __cplusplus >= 201103L - #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) - #endif -#endif -#if !defined(JSON_HEDLEY_CONSTEXPR) - #define JSON_HEDLEY_CONSTEXPR -#endif - -#if defined(JSON_HEDLEY_PREDICT) - #undef JSON_HEDLEY_PREDICT -#endif -#if defined(JSON_HEDLEY_LIKELY) - #undef JSON_HEDLEY_LIKELY -#endif -#if defined(JSON_HEDLEY_UNLIKELY) - #undef JSON_HEDLEY_UNLIKELY -#endif -#if defined(JSON_HEDLEY_UNPREDICTABLE) - #undef JSON_HEDLEY_UNPREDICTABLE -#endif -#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) - #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) -#endif -#if \ - (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) -# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) -# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) -# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) -# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) -#elif \ - (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ - (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) -# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ - (__extension__ ({ \ - double hedley_probability_ = (probability); \ - ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ - })) -# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ - (__extension__ ({ \ - double hedley_probability_ = (probability); \ - ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ - })) -# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) -# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) -#else -# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) -# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) -# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) -# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) -# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) -#endif -#if !defined(JSON_HEDLEY_UNPREDICTABLE) - #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) -#endif - -#if defined(JSON_HEDLEY_MALLOC) - #undef JSON_HEDLEY_MALLOC -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) - #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_MALLOC __declspec(restrict) -#else - #define JSON_HEDLEY_MALLOC -#endif - -#if defined(JSON_HEDLEY_PURE) - #undef JSON_HEDLEY_PURE -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PURE __attribute__((__pure__)) -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) -# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") -#elif defined(__cplusplus) && \ - ( \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ - ) -# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") -#else -# define JSON_HEDLEY_PURE -#endif - -#if defined(JSON_HEDLEY_CONST) - #undef JSON_HEDLEY_CONST -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_CONST __attribute__((__const__)) -#elif \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) - #define JSON_HEDLEY_CONST _Pragma("no_side_effect") -#else - #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE -#endif - -#if defined(JSON_HEDLEY_RESTRICT) - #undef JSON_HEDLEY_RESTRICT -#endif -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) - #define JSON_HEDLEY_RESTRICT restrict -#elif \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ - defined(__clang__) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_RESTRICT __restrict -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) - #define JSON_HEDLEY_RESTRICT _Restrict -#else - #define JSON_HEDLEY_RESTRICT -#endif - -#if defined(JSON_HEDLEY_INLINE) - #undef JSON_HEDLEY_INLINE -#endif -#if \ - (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ - (defined(__cplusplus) && (__cplusplus >= 199711L)) - #define JSON_HEDLEY_INLINE inline -#elif \ - defined(JSON_HEDLEY_GCC_VERSION) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) - #define JSON_HEDLEY_INLINE __inline__ -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_INLINE __inline -#else - #define JSON_HEDLEY_INLINE -#endif - -#if defined(JSON_HEDLEY_ALWAYS_INLINE) - #undef JSON_HEDLEY_ALWAYS_INLINE -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) -# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -# define JSON_HEDLEY_ALWAYS_INLINE __forceinline -#elif defined(__cplusplus) && \ - ( \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ - ) -# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) -# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") -#else -# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE -#endif - -#if defined(JSON_HEDLEY_NEVER_INLINE) - #undef JSON_HEDLEY_NEVER_INLINE -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) - #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) - #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") -#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) - #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") -#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) - #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) - #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) -#else - #define JSON_HEDLEY_NEVER_INLINE -#endif - -#if defined(JSON_HEDLEY_PRIVATE) - #undef JSON_HEDLEY_PRIVATE -#endif -#if defined(JSON_HEDLEY_PUBLIC) - #undef JSON_HEDLEY_PUBLIC -#endif -#if defined(JSON_HEDLEY_IMPORT) - #undef JSON_HEDLEY_IMPORT -#endif -#if defined(_WIN32) || defined(__CYGWIN__) -# define JSON_HEDLEY_PRIVATE -# define JSON_HEDLEY_PUBLIC __declspec(dllexport) -# define JSON_HEDLEY_IMPORT __declspec(dllimport) -#else -# if \ - JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ - ( \ - defined(__TI_EABI__) && \ - ( \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ - ) \ - ) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) -# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) -# else -# define JSON_HEDLEY_PRIVATE -# define JSON_HEDLEY_PUBLIC -# endif -# define JSON_HEDLEY_IMPORT extern -#endif - -#if defined(JSON_HEDLEY_NO_THROW) - #undef JSON_HEDLEY_NO_THROW -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) - #define JSON_HEDLEY_NO_THROW __declspec(nothrow) -#else - #define JSON_HEDLEY_NO_THROW -#endif - -#if defined(JSON_HEDLEY_FALL_THROUGH) - #undef JSON_HEDLEY_FALL_THROUGH -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) -#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) - #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) -#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) - #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) -#elif defined(__fallthrough) /* SAL */ - #define JSON_HEDLEY_FALL_THROUGH __fallthrough -#else - #define JSON_HEDLEY_FALL_THROUGH -#endif - -#if defined(JSON_HEDLEY_RETURNS_NON_NULL) - #undef JSON_HEDLEY_RETURNS_NON_NULL -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) -#elif defined(_Ret_notnull_) /* SAL */ - #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ -#else - #define JSON_HEDLEY_RETURNS_NON_NULL -#endif - -#if defined(JSON_HEDLEY_ARRAY_PARAM) - #undef JSON_HEDLEY_ARRAY_PARAM -#endif -#if \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ - !defined(__STDC_NO_VLA__) && \ - !defined(__cplusplus) && \ - !defined(JSON_HEDLEY_PGI_VERSION) && \ - !defined(JSON_HEDLEY_TINYC_VERSION) - #define JSON_HEDLEY_ARRAY_PARAM(name) (name) -#else - #define JSON_HEDLEY_ARRAY_PARAM(name) -#endif - -#if defined(JSON_HEDLEY_IS_CONSTANT) - #undef JSON_HEDLEY_IS_CONSTANT -#endif -#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) - #undef JSON_HEDLEY_REQUIRE_CONSTEXPR -#endif -/* JSON_HEDLEY_IS_CONSTEXPR_ is for - HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ -#if defined(JSON_HEDLEY_IS_CONSTEXPR_) - #undef JSON_HEDLEY_IS_CONSTEXPR_ -#endif -#if \ - JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ - (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) -#endif -#if !defined(__cplusplus) -# if \ - JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) -#if defined(__INTPTR_TYPE__) - #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) -#else - #include - #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) -#endif -# elif \ - ( \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ - !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ - !defined(JSON_HEDLEY_PGI_VERSION) && \ - !defined(JSON_HEDLEY_IAR_VERSION)) || \ - (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) -#if defined(__INTPTR_TYPE__) - #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) -#else - #include - #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) -#endif -# elif \ - defined(JSON_HEDLEY_GCC_VERSION) || \ - defined(JSON_HEDLEY_INTEL_VERSION) || \ - defined(JSON_HEDLEY_TINYC_VERSION) || \ - defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ - defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ - defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ - defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ - defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ - defined(__clang__) -# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ - sizeof(void) != \ - sizeof(*( \ - 1 ? \ - ((void*) ((expr) * 0L) ) : \ -((struct { char v[sizeof(void) * 2]; } *) 1) \ - ) \ - ) \ - ) -# endif -#endif -#if defined(JSON_HEDLEY_IS_CONSTEXPR_) - #if !defined(JSON_HEDLEY_IS_CONSTANT) - #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) - #endif - #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) -#else - #if !defined(JSON_HEDLEY_IS_CONSTANT) - #define JSON_HEDLEY_IS_CONSTANT(expr) (0) - #endif - #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) -#endif - -#if defined(JSON_HEDLEY_BEGIN_C_DECLS) - #undef JSON_HEDLEY_BEGIN_C_DECLS -#endif -#if defined(JSON_HEDLEY_END_C_DECLS) - #undef JSON_HEDLEY_END_C_DECLS -#endif -#if defined(JSON_HEDLEY_C_DECL) - #undef JSON_HEDLEY_C_DECL -#endif -#if defined(__cplusplus) - #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { - #define JSON_HEDLEY_END_C_DECLS } - #define JSON_HEDLEY_C_DECL extern "C" -#else - #define JSON_HEDLEY_BEGIN_C_DECLS - #define JSON_HEDLEY_END_C_DECLS - #define JSON_HEDLEY_C_DECL -#endif - -#if defined(JSON_HEDLEY_STATIC_ASSERT) - #undef JSON_HEDLEY_STATIC_ASSERT -#endif -#if \ - !defined(__cplusplus) && ( \ - (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ - (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - defined(_Static_assert) \ - ) -# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) -#elif \ - (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ - JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) -#else -# define JSON_HEDLEY_STATIC_ASSERT(expr, message) -#endif - -#if defined(JSON_HEDLEY_NULL) - #undef JSON_HEDLEY_NULL -#endif -#if defined(__cplusplus) - #if __cplusplus >= 201103L - #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) - #elif defined(NULL) - #define JSON_HEDLEY_NULL NULL - #else - #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) - #endif -#elif defined(NULL) - #define JSON_HEDLEY_NULL NULL -#else - #define JSON_HEDLEY_NULL ((void*) 0) -#endif - -#if defined(JSON_HEDLEY_MESSAGE) - #undef JSON_HEDLEY_MESSAGE -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") -# define JSON_HEDLEY_MESSAGE(msg) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ - JSON_HEDLEY_PRAGMA(message msg) \ - JSON_HEDLEY_DIAGNOSTIC_POP -#elif \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) -#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#else -# define JSON_HEDLEY_MESSAGE(msg) -#endif - -#if defined(JSON_HEDLEY_WARNING) - #undef JSON_HEDLEY_WARNING -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") -# define JSON_HEDLEY_WARNING(msg) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ - JSON_HEDLEY_PRAGMA(clang warning msg) \ - JSON_HEDLEY_DIAGNOSTIC_POP -#elif \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#else -# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) -#endif - -#if defined(JSON_HEDLEY_REQUIRE) - #undef JSON_HEDLEY_REQUIRE -#endif -#if defined(JSON_HEDLEY_REQUIRE_MSG) - #undef JSON_HEDLEY_REQUIRE_MSG -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) -# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") -# define JSON_HEDLEY_REQUIRE(expr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ - __attribute__((diagnose_if(!(expr), #expr, "error"))) \ - JSON_HEDLEY_DIAGNOSTIC_POP -# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ - __attribute__((diagnose_if(!(expr), msg, "error"))) \ - JSON_HEDLEY_DIAGNOSTIC_POP -# else -# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) -# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) -# endif -#else -# define JSON_HEDLEY_REQUIRE(expr) -# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) -#endif - -#if defined(JSON_HEDLEY_FLAGS) - #undef JSON_HEDLEY_FLAGS -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) - #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) -#else - #define JSON_HEDLEY_FLAGS -#endif - -#if defined(JSON_HEDLEY_FLAGS_CAST) - #undef JSON_HEDLEY_FLAGS_CAST -#endif -#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) -# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("warning(disable:188)") \ - ((T) (expr)); \ - JSON_HEDLEY_DIAGNOSTIC_POP \ - })) -#else -# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) -#endif - -#if defined(JSON_HEDLEY_EMPTY_BASES) - #undef JSON_HEDLEY_EMPTY_BASES -#endif -#if \ - (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) -#else - #define JSON_HEDLEY_EMPTY_BASES -#endif - -/* Remaining macros are deprecated. */ - -#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) - #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK -#endif -#if defined(__clang__) - #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) -#else - #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) - #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) - #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) - #undef JSON_HEDLEY_CLANG_HAS_BUILTIN -#endif -#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) - -#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) - #undef JSON_HEDLEY_CLANG_HAS_FEATURE -#endif -#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) - -#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) - #undef JSON_HEDLEY_CLANG_HAS_EXTENSION -#endif -#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) - -#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) - #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) - #undef JSON_HEDLEY_CLANG_HAS_WARNING -#endif -#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) - -#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ - - // This file contains all internal macro definitions // You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them @@ -2225,34 +125,31 @@ JSON_HEDLEY_DIAGNOSTIC_POP #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" #endif #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) - #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900 #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" #endif #endif #endif -// C++ language standard detection -// if the user manually specified the used c++ version this is skipped -#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) - #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) - #define JSON_HAS_CPP_20 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) - #define JSON_HAS_CPP_14 - #endif - // the cpp 11 flag is always specified because it is the minimal required version - #define JSON_HAS_CPP_11 +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" #endif // disable documentation warnings on clang #if defined(__clang__) - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wdocumentation" - #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow for portable deprecation warnings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define JSON_DEPRECATED __declspec(deprecated) +#else + #define JSON_DEPRECATED #endif // allow to disable exceptions @@ -2262,7 +159,6 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define JSON_CATCH(exception) catch(exception) #define JSON_INTERNAL_CATCH(exception) catch(exception) #else - #include #define JSON_THROW(exception) std::abort() #define JSON_TRY if(true) #define JSON_CATCH(exception) if(false) @@ -2281,7 +177,6 @@ JSON_HEDLEY_DIAGNOSTIC_POP #if defined(JSON_CATCH_USER) #undef JSON_CATCH #define JSON_CATCH JSON_CATCH_USER - #undef JSON_INTERNAL_CATCH #define JSON_INTERNAL_CATCH JSON_CATCH_USER #endif #if defined(JSON_INTERNAL_CATCH_USER) @@ -2289,49 +184,22 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER #endif -// allow to override assert -#if !defined(JSON_ASSERT) - #include // assert - #define JSON_ASSERT(x) assert(x) -#endif - -// allow to access some private functions (needed by the test suite) -#if defined(JSON_TESTS_PRIVATE) - #define JSON_PRIVATE_UNLESS_TESTED public +// manual branch prediction +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_LIKELY(x) __builtin_expect(!!(x), 1) + #define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0) #else - #define JSON_PRIVATE_UNLESS_TESTED private + #define JSON_LIKELY(x) x + #define JSON_UNLIKELY(x) x #endif -/*! -@brief macro to briefly define a mapping between an enum and JSON -@def NLOHMANN_JSON_SERIALIZE_ENUM -@since version 3.4.0 -*/ -#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ - template \ - inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ - { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [e](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.first == e; \ - }); \ - j = ((it != std::end(m)) ? it : std::begin(m))->second; \ - } \ - template \ - inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ - { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [&j](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.second == j; \ - }); \ - e = ((it != std::end(m)) ? it : std::begin(m))->first; \ - } +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif // Ugly macros to avoid uglier copy-paste when specializing basic_json. They // may be removed in the future once the class is split. @@ -2342,274 +210,337 @@ JSON_HEDLEY_DIAGNOSTIC_POP class StringType, class BooleanType, class NumberIntegerType, \ class NumberUnsignedType, class NumberFloatType, \ template class AllocatorType, \ - template class JSONSerializer, \ - class BinaryType> + template class JSONSerializer> #define NLOHMANN_BASIC_JSON_TPL \ basic_json - -// Macros to simplify conversion from/to types - -#define NLOHMANN_JSON_EXPAND( x ) x -#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME -#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ - NLOHMANN_JSON_PASTE64, \ - NLOHMANN_JSON_PASTE63, \ - NLOHMANN_JSON_PASTE62, \ - NLOHMANN_JSON_PASTE61, \ - NLOHMANN_JSON_PASTE60, \ - NLOHMANN_JSON_PASTE59, \ - NLOHMANN_JSON_PASTE58, \ - NLOHMANN_JSON_PASTE57, \ - NLOHMANN_JSON_PASTE56, \ - NLOHMANN_JSON_PASTE55, \ - NLOHMANN_JSON_PASTE54, \ - NLOHMANN_JSON_PASTE53, \ - NLOHMANN_JSON_PASTE52, \ - NLOHMANN_JSON_PASTE51, \ - NLOHMANN_JSON_PASTE50, \ - NLOHMANN_JSON_PASTE49, \ - NLOHMANN_JSON_PASTE48, \ - NLOHMANN_JSON_PASTE47, \ - NLOHMANN_JSON_PASTE46, \ - NLOHMANN_JSON_PASTE45, \ - NLOHMANN_JSON_PASTE44, \ - NLOHMANN_JSON_PASTE43, \ - NLOHMANN_JSON_PASTE42, \ - NLOHMANN_JSON_PASTE41, \ - NLOHMANN_JSON_PASTE40, \ - NLOHMANN_JSON_PASTE39, \ - NLOHMANN_JSON_PASTE38, \ - NLOHMANN_JSON_PASTE37, \ - NLOHMANN_JSON_PASTE36, \ - NLOHMANN_JSON_PASTE35, \ - NLOHMANN_JSON_PASTE34, \ - NLOHMANN_JSON_PASTE33, \ - NLOHMANN_JSON_PASTE32, \ - NLOHMANN_JSON_PASTE31, \ - NLOHMANN_JSON_PASTE30, \ - NLOHMANN_JSON_PASTE29, \ - NLOHMANN_JSON_PASTE28, \ - NLOHMANN_JSON_PASTE27, \ - NLOHMANN_JSON_PASTE26, \ - NLOHMANN_JSON_PASTE25, \ - NLOHMANN_JSON_PASTE24, \ - NLOHMANN_JSON_PASTE23, \ - NLOHMANN_JSON_PASTE22, \ - NLOHMANN_JSON_PASTE21, \ - NLOHMANN_JSON_PASTE20, \ - NLOHMANN_JSON_PASTE19, \ - NLOHMANN_JSON_PASTE18, \ - NLOHMANN_JSON_PASTE17, \ - NLOHMANN_JSON_PASTE16, \ - NLOHMANN_JSON_PASTE15, \ - NLOHMANN_JSON_PASTE14, \ - NLOHMANN_JSON_PASTE13, \ - NLOHMANN_JSON_PASTE12, \ - NLOHMANN_JSON_PASTE11, \ - NLOHMANN_JSON_PASTE10, \ - NLOHMANN_JSON_PASTE9, \ - NLOHMANN_JSON_PASTE8, \ - NLOHMANN_JSON_PASTE7, \ - NLOHMANN_JSON_PASTE6, \ - NLOHMANN_JSON_PASTE5, \ - NLOHMANN_JSON_PASTE4, \ - NLOHMANN_JSON_PASTE3, \ - NLOHMANN_JSON_PASTE2, \ - NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) -#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) -#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) -#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) -#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) -#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) -#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) -#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) -#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) -#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) -#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) -#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) -#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) -#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) -#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) -#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) -#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) -#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) -#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) -#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) -#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) -#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) -#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) -#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) -#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) -#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) -#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) -#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) -#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) -#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) -#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) -#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) -#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) -#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) -#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) -#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) -#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) -#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) -#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) -#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) -#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) -#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) -#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) -#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) -#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) -#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) -#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) -#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) -#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) -#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) -#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) -#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) -#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) -#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) -#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) -#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) -#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) -#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) -#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) -#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) -#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) -#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) -#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) -#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) - -#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; -#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); + AllocatorType, JSONSerializer> /*! -@brief macro -@def NLOHMANN_DEFINE_TYPE_INTRUSIVE -@since version 3.9.0 +@brief Helper to determine whether there's a key_type for T. + +This helper is used to tell associative containers apart from other containers +such as sequence containers. For instance, `std::map` passes the test as it +contains a `mapped_type`, whereas `std::vector` fails the test. + +@sa http://stackoverflow.com/a/7728728/266378 +@since version 1.0.0, overworked in version 2.0.6 */ -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } +#define NLOHMANN_JSON_HAS_HELPER(type) \ + template struct has_##type { \ + private: \ + template \ + static int detect(U &&); \ + static void detect(...); \ + public: \ + static constexpr bool value = \ + std::is_integral()))>::value; \ + } -/*! -@brief macro -@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE -@since version 3.9.0 -*/ -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ - inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } - -#ifndef JSON_USE_IMPLICIT_CONVERSIONS - #define JSON_USE_IMPLICIT_CONVERSIONS 1 -#endif - -#if JSON_USE_IMPLICIT_CONVERSIONS - #define JSON_EXPLICIT -#else - #define JSON_EXPLICIT explicit -#endif - -#ifndef JSON_DIAGNOSTICS - #define JSON_DIAGNOSTICS 0 -#endif - - -namespace nlohmann -{ -namespace detail -{ - -/*! -@brief replace all occurrences of a substring by another string - -@param[in,out] s the string to manipulate; changed so that all - occurrences of @a f are replaced with @a t -@param[in] f the substring to replace with @a t -@param[in] t the string to replace @a f - -@pre The search string @a f must not be empty. **This precondition is -enforced with an assertion.** - -@since version 2.0.0 -*/ -inline void replace_substring(std::string& s, const std::string& f, - const std::string& t) -{ - JSON_ASSERT(!f.empty()); - for (auto pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find next occurrence of f - {} -} - -/*! - * @brief string escaping as described in RFC 6901 (Sect. 4) - * @param[in] s string to escape - * @return escaped string - * - * Note the order of escaping "~" to "~0" and "/" to "~1" is important. - */ -inline std::string escape(std::string s) -{ - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); - return s; -} - -/*! - * @brief string unescaping as described in RFC 6901 (Sect. 4) - * @param[in] s string to unescape - * @return unescaped string - * - * Note the order of escaping "~1" to "/" and "~0" to "~" is important. - */ -static void unescape(std::string& s) -{ - replace_substring(s, "~1", "/"); - replace_substring(s, "~0", "~"); -} - -} // namespace detail -} // namespace nlohmann - -// #include +// #include +#include // not #include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type namespace nlohmann { namespace detail { -/// struct to capture the start position of the current token -struct position_t -{ - /// the total number of characters read - std::size_t chars_read_total = 0; - /// the number of characters read in the current line - std::size_t chars_read_current_line = 0; - /// the number of lines read - std::size_t lines_read = 0; +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; - /// conversion to size_t to preserve SAX interface - constexpr operator size_t() const +template +using uncvref_t = typename std::remove_cv::type>::type; + +// implementation of C++14 index_sequence and affiliates +// source: https://stackoverflow.com/a/32223343 +template +struct index_sequence +{ + using type = index_sequence; + using value_type = std::size_t; + static constexpr std::size_t size() noexcept { - return chars_read_total; + return sizeof...(Ints); } }; -} // namespace detail -} // namespace nlohmann +template +struct merge_and_renumber; + +template +struct merge_and_renumber, index_sequence> + : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; + +template +struct make_index_sequence + : merge_and_renumber < typename make_index_sequence < N / 2 >::type, + typename make_index_sequence < N - N / 2 >::type > {}; + +template<> struct make_index_sequence<0> : index_sequence<> {}; +template<> struct make_index_sequence<1> : index_sequence<0> {}; + +template +using index_sequence_for = make_index_sequence; + +/* +Implementation of two C++17 constructs: conjunction, negation. This is needed +to avoid evaluating all the traits in a condition + +For example: not std::is_same::value and has_value_type::value +will not compile when T = void (on MSVC at least). Whereas +conjunction>, has_value_type>::value will +stop evaluating if negation<...>::value == false + +Please note that those constructs must be used with caution, since symbols can +become very long quickly (which can slow down compilation and cause MSVC +internal compiler errors). Only use it when you have to (see example ahead). +*/ +template struct conjunction : std::true_type {}; +template struct conjunction : B1 {}; +template +struct conjunction : std::conditional, B1>::type {}; + +template struct negation : std::integral_constant {}; + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; +} +} + +// #include + + +#include // not +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval + +// #include + +// #include // #include +namespace nlohmann +{ +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ +///////////// +// helpers // +///////////// + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +//////////////////////// +// has_/is_ functions // +//////////////////////// + +// source: https://stackoverflow.com/a/37193089/4116453 + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +NLOHMANN_JSON_HAS_HELPER(mapped_type); +NLOHMANN_JSON_HAS_HELPER(key_type); +NLOHMANN_JSON_HAS_HELPER(value_type); +NLOHMANN_JSON_HAS_HELPER(iterator); + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl +{ + static constexpr auto value = + std::is_constructible::value and + std::is_constructible::value; +}; + +template +struct is_compatible_string_type_impl : std::false_type {}; + +template +struct is_compatible_string_type_impl +{ + static constexpr auto value = + std::is_same::value and + std::is_constructible::value; +}; + +template +struct is_compatible_object_type +{ + static auto constexpr value = is_compatible_object_type_impl < + conjunction>, + has_mapped_type, + has_key_type>::value, + typename BasicJsonType::object_t, CompatibleObjectType >::value; +}; + +template +struct is_compatible_string_type +{ + static auto constexpr value = is_compatible_string_type_impl < + conjunction>, + has_value_type>::value, + typename BasicJsonType::string_t, CompatibleStringType >::value; +}; + +template +struct is_basic_json_nested_type +{ + static auto constexpr value = std::is_same::value or + std::is_same::value or + std::is_same::value or + std::is_same::value; +}; + +template +struct is_compatible_array_type +{ + static auto constexpr value = + conjunction>, + negation>, + negation>, + negation>, + has_value_type, + has_iterator>::value; +}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + std::is_constructible::value and + CompatibleLimits::is_integer and + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type +{ + static constexpr auto value = + is_compatible_integer_type_impl < + std::is_integral::value and + not std::is_same::value, + RealIntegerType, CompatibleNumberIntegerType > ::value; +}; + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json +{ + private: + // also check the return type of from_json + template::from_json( + std::declval(), std::declval()))>::value>> + static int detect(U&&); + static void detect(...); + + public: + static constexpr bool value = std::is_integral>()))>::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json +{ + private: + template < + typename U, + typename = enable_if_t::from_json(std::declval()))>::value >> + static int detect(U&&); + static void detect(...); + + public: + static constexpr bool value = std::is_integral>()))>::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +template +struct has_to_json +{ + private: + template::to_json( + std::declval(), std::declval()))> + static int detect(U&&); + static void detect(...); + + public: + static constexpr bool value = std::is_integral>()))>::value; +}; + +template +struct is_compatible_complete_type +{ + static constexpr bool value = + not std::is_base_of::value and + not is_basic_json::value and + not is_basic_json_nested_type::value and + has_to_json::value; +}; + +template +struct is_compatible_type + : conjunction, + is_compatible_complete_type> +{ +}; +} +} + +// #include + + +#include // exception +#include // runtime_error +#include // to_string + namespace nlohmann { namespace detail @@ -2656,10 +587,9 @@ class exception : public std::exception } /// the id of the exception - const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) + const int id; protected: - JSON_HEDLEY_NON_NULL(3) exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} static std::string name(const std::string& ename, int id_) @@ -2667,70 +597,6 @@ class exception : public std::exception return "[json.exception." + ename + "." + std::to_string(id_) + "] "; } - template - static std::string diagnostics(const BasicJsonType& leaf_element) - { -#if JSON_DIAGNOSTICS - std::vector tokens; - for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent) - { - switch (current->m_parent->type()) - { - case value_t::array: - { - for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) - { - if (¤t->m_parent->m_value.array->operator[](i) == current) - { - tokens.emplace_back(std::to_string(i)); - break; - } - } - break; - } - - case value_t::object: - { - for (const auto& element : *current->m_parent->m_value.object) - { - if (&element.second == current) - { - tokens.emplace_back(element.first.c_str()); - break; - } - } - break; - } - - case value_t::null: // LCOV_EXCL_LINE - case value_t::string: // LCOV_EXCL_LINE - case value_t::boolean: // LCOV_EXCL_LINE - case value_t::number_integer: // LCOV_EXCL_LINE - case value_t::number_unsigned: // LCOV_EXCL_LINE - case value_t::number_float: // LCOV_EXCL_LINE - case value_t::binary: // LCOV_EXCL_LINE - case value_t::discarded: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - break; // LCOV_EXCL_LINE - } - } - - if (tokens.empty()) - { - return ""; - } - - return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, - [](const std::string & a, const std::string & b) - { - return a + "/" + detail::escape(b); - }) + ") "; -#else - static_cast(leaf_element); - return ""; -#endif - } - private: /// an exception object as storage for error messages std::runtime_error m; @@ -2762,8 +628,6 @@ json.exception.parse_error.109 | parse error: array index 'one' is not a number json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. -json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). -json.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed. @note For an input with n bytes, 1 is the index of the first character and n+1 is the index of the terminating null byte or the end of file. This also @@ -2772,12 +636,12 @@ json.exception.parse_error.115 | parse error at byte 5: syntax error while parsi @liveexample{The following code shows how a `parse_error` exception can be caught.,parse_error} -@sa - @ref exception for the base class of the library exceptions -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref type_error for exceptions indicating executing a member function with +@sa @ref exception for the base class of the library exceptions +@sa @ref invalid_iterator for exceptions indicating errors with iterators +@sa @ref type_error for exceptions indicating executing a member function with a wrong type -@sa - @ref out_of_range for exceptions indicating access out of the defined range -@sa - @ref other_error for exceptions indicating other library errors +@sa @ref out_of_range for exceptions indicating access out of the defined range +@sa @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ @@ -2787,26 +651,16 @@ class parse_error : public exception /*! @brief create a parse error exception @param[in] id_ the id of the exception - @param[in] pos the position where the error occurred (or with - chars_read_total=0 if the position cannot be - determined) + @param[in] byte_ the byte index where the error occurred (or 0 if the + position cannot be determined) @param[in] what_arg the explanatory string @return parse_error object */ - template - static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context) + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) { std::string w = exception::name("parse_error", id_) + "parse error" + - position_string(pos) + ": " + exception::diagnostics(context) + what_arg; - return parse_error(id_, pos.chars_read_total, w.c_str()); - } - - template - static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("parse_error", id_) + "parse error" + - (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + - ": " + exception::diagnostics(context) + what_arg; + (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") + + ": " + what_arg; return parse_error(id_, byte_, w.c_str()); } @@ -2824,12 +678,6 @@ class parse_error : public exception private: parse_error(int id_, std::size_t byte_, const char* what_arg) : exception(id_, what_arg), byte(byte_) {} - - static std::string position_string(const position_t& pos) - { - return " at line " + std::to_string(pos.lines_read + 1) + - ", column " + std::to_string(pos.chars_read_current_line); - } }; /*! @@ -2860,27 +708,25 @@ json.exception.invalid_iterator.214 | cannot get value | Cannot get value for it @liveexample{The following code shows how an `invalid_iterator` exception can be caught.,invalid_iterator} -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref type_error for exceptions indicating executing a member function with +@sa @ref exception for the base class of the library exceptions +@sa @ref parse_error for exceptions indicating a parse error +@sa @ref type_error for exceptions indicating executing a member function with a wrong type -@sa - @ref out_of_range for exceptions indicating access out of the defined range -@sa - @ref other_error for exceptions indicating other library errors +@sa @ref out_of_range for exceptions indicating access out of the defined range +@sa @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class invalid_iterator : public exception { public: - template - static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) + static invalid_iterator create(int id_, const std::string& what_arg) { - std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; + std::string w = exception::name("invalid_iterator", id_) + what_arg; return invalid_iterator(id_, w.c_str()); } private: - JSON_HEDLEY_NON_NULL(3) invalid_iterator(int id_, const char* what_arg) : exception(id_, what_arg) {} }; @@ -2897,7 +743,7 @@ name / id | example message | description ----------------------------- | --------------- | ------------------------- json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. -json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. +json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t&. json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. @@ -2911,31 +757,28 @@ json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | -json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | @liveexample{The following code shows how a `type_error` exception can be caught.,type_error} -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref out_of_range for exceptions indicating access out of the defined range -@sa - @ref other_error for exceptions indicating other library errors +@sa @ref exception for the base class of the library exceptions +@sa @ref parse_error for exceptions indicating a parse error +@sa @ref invalid_iterator for exceptions indicating errors with iterators +@sa @ref out_of_range for exceptions indicating access out of the defined range +@sa @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class type_error : public exception { public: - template - static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + static type_error create(int id_, const std::string& what_arg) { - std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; + std::string w = exception::name("type_error", id_) + what_arg; return type_error(id_, w.c_str()); } private: - JSON_HEDLEY_NON_NULL(3) type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} }; @@ -2956,34 +799,31 @@ json.exception.out_of_range.403 | key 'foo' not found | The provided key was not json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. -json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) | +json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON only supports integers numbers up to 9223372036854775807. | json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | -json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | @liveexample{The following code shows how an `out_of_range` exception can be caught.,out_of_range} -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref type_error for exceptions indicating executing a member function with +@sa @ref exception for the base class of the library exceptions +@sa @ref parse_error for exceptions indicating a parse error +@sa @ref invalid_iterator for exceptions indicating errors with iterators +@sa @ref type_error for exceptions indicating executing a member function with a wrong type -@sa - @ref other_error for exceptions indicating other library errors +@sa @ref other_error for exceptions indicating other library errors @since version 3.0.0 */ class out_of_range : public exception { public: - template - static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) + static out_of_range create(int id_, const std::string& what_arg) { - std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; + std::string w = exception::name("out_of_range", id_) + what_arg; return out_of_range(id_, w.c_str()); } private: - JSON_HEDLEY_NON_NULL(3) out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} }; @@ -2999,12 +839,12 @@ name / id | example message | description ------------------------------ | --------------- | ------------------------- json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref type_error for exceptions indicating executing a member function with +@sa @ref exception for the base class of the library exceptions +@sa @ref parse_error for exceptions indicating a parse error +@sa @ref invalid_iterator for exceptions indicating errors with iterators +@sa @ref type_error for exceptions indicating executing a member function with a wrong type -@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa @ref out_of_range for exceptions indicating access out of the defined range @liveexample{The following code shows how an `other_error` exception can be caught.,other_error} @@ -3014,874 +854,119 @@ caught.,other_error} class other_error : public exception { public: - template - static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + static other_error create(int id_, const std::string& what_arg) { - std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; + std::string w = exception::name("other_error", id_) + what_arg; return other_error(id_, w.c_str()); } private: - JSON_HEDLEY_NON_NULL(3) other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} }; -} // namespace detail -} // namespace nlohmann +} +} -// #include - -// #include +// #include +#include // array +#include // and #include // size_t -#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type -#include // index_sequence, make_index_sequence, index_sequence_for - -// #include - +#include // uint8_t namespace nlohmann { namespace detail { - -template -using uncvref_t = typename std::remove_cv::type>::type; - -#ifdef JSON_HAS_CPP_14 - -// the following utilities are natively available in C++14 -using std::enable_if_t; -using std::index_sequence; -using std::make_index_sequence; -using std::index_sequence_for; - -#else - -// alias templates to reduce boilerplate -template -using enable_if_t = typename std::enable_if::type; - -// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h -// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. - -//// START OF CODE FROM GOOGLE ABSEIL - -// integer_sequence -// -// Class template representing a compile-time integer sequence. An instantiation -// of `integer_sequence` has a sequence of integers encoded in its -// type through its template arguments (which is a common need when -// working with C++11 variadic templates). `absl::integer_sequence` is designed -// to be a drop-in replacement for C++14's `std::integer_sequence`. -// -// Example: -// -// template< class T, T... Ints > -// void user_function(integer_sequence); -// -// int main() -// { -// // user_function's `T` will be deduced to `int` and `Ints...` -// // will be deduced to `0, 1, 2, 3, 4`. -// user_function(make_integer_sequence()); -// } -template -struct integer_sequence -{ - using value_type = T; - static constexpr std::size_t size() noexcept - { - return sizeof...(Ints); - } -}; - -// index_sequence -// -// A helper template for an `integer_sequence` of `size_t`, -// `absl::index_sequence` is designed to be a drop-in replacement for C++14's -// `std::index_sequence`. -template -using index_sequence = integer_sequence; - -namespace utility_internal -{ - -template -struct Extend; - -// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. -template -struct Extend, SeqSize, 0> -{ - using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; -}; - -template -struct Extend, SeqSize, 1> -{ - using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; -}; - -// Recursion helper for 'make_integer_sequence'. -// 'Gen::type' is an alias for 'integer_sequence'. -template -struct Gen -{ - using type = - typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; -}; - -template -struct Gen -{ - using type = integer_sequence; -}; - -} // namespace utility_internal - -// Compile-time sequences of integers - -// make_integer_sequence -// -// This template alias is equivalent to -// `integer_sequence`, and is designed to be a drop-in -// replacement for C++14's `std::make_integer_sequence`. -template -using make_integer_sequence = typename utility_internal::Gen::type; - -// make_index_sequence -// -// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, -// and is designed to be a drop-in replacement for C++14's -// `std::make_index_sequence`. -template -using make_index_sequence = make_integer_sequence; - -// index_sequence_for -// -// Converts a typename pack into an index sequence of the same length, and -// is designed to be a drop-in replacement for C++14's -// `std::index_sequence_for()` -template -using index_sequence_for = make_index_sequence; - -//// END OF CODE FROM GOOGLE ABSEIL - -#endif - -// dispatch utility (taken from ranges-v3) -template struct priority_tag : priority_tag < N - 1 > {}; -template<> struct priority_tag<0> {}; - -// taken from ranges-v3 -template -struct static_const -{ - static constexpr T value{}; -}; - -template -constexpr T static_const::value; - -} // namespace detail -} // namespace nlohmann - -// #include - - -namespace nlohmann -{ -namespace detail -{ -// dispatching helper struct -template struct identity_tag {}; -} // namespace detail -} // namespace nlohmann - -// #include - - -#include // numeric_limits -#include // false_type, is_constructible, is_integral, is_same, true_type -#include // declval -#include // tuple - -// #include - - -#include // random_access_iterator_tag - -// #include - - -namespace nlohmann -{ -namespace detail -{ -template struct make_void -{ - using type = void; -}; -template using void_t = typename make_void::type; -} // namespace detail -} // namespace nlohmann - -// #include - - -namespace nlohmann -{ -namespace detail -{ -template -struct iterator_types {}; - -template -struct iterator_types < - It, - void_t> -{ - using difference_type = typename It::difference_type; - using value_type = typename It::value_type; - using pointer = typename It::pointer; - using reference = typename It::reference; - using iterator_category = typename It::iterator_category; -}; - -// This is required as some compilers implement std::iterator_traits in a way that -// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. -template -struct iterator_traits -{ -}; - -template -struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> - : iterator_types -{ -}; - -template -struct iterator_traits::value>> -{ - using iterator_category = std::random_access_iterator_tag; - using value_type = T; - using difference_type = ptrdiff_t; - using pointer = T*; - using reference = T&; -}; -} // namespace detail -} // namespace nlohmann - -// #include - -// #include - -// #include - - -#include - -// #include - - -// https://en.cppreference.com/w/cpp/experimental/is_detected -namespace nlohmann -{ -namespace detail -{ -struct nonesuch -{ - nonesuch() = delete; - ~nonesuch() = delete; - nonesuch(nonesuch const&) = delete; - nonesuch(nonesuch const&&) = delete; - void operator=(nonesuch const&) = delete; - void operator=(nonesuch&&) = delete; -}; - -template class Op, - class... Args> -struct detector -{ - using value_t = std::false_type; - using type = Default; -}; - -template class Op, class... Args> -struct detector>, Op, Args...> -{ - using value_t = std::true_type; - using type = Op; -}; - -template class Op, class... Args> -using is_detected = typename detector::value_t; - -template class Op, class... Args> -struct is_detected_lazy : is_detected { }; - -template class Op, class... Args> -using detected_t = typename detector::type; - -template class Op, class... Args> -using detected_or = detector; - -template class Op, class... Args> -using detected_or_t = typename detected_or::type; - -template class Op, class... Args> -using is_detected_exact = std::is_same>; - -template class Op, class... Args> -using is_detected_convertible = - std::is_convertible, To>; -} // namespace detail -} // namespace nlohmann - -// #include -#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ -#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ - -#include // int64_t, uint64_t -#include // map -#include // allocator -#include // string -#include // vector +/////////////////////////// +// JSON type enumeration // +/////////////////////////// /*! -@brief namespace for Niels Lohmann -@see https://github.com/nlohmann -@since version 1.0.0 -*/ -namespace nlohmann -{ -/*! -@brief default JSONSerializer template argument +@brief the JSON type enumeration -This serializer ignores the template arguments and uses ADL -([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) -for serialization. -*/ -template -struct adl_serializer; +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. -template class ObjectType = - std::map, - template class ArrayType = std::vector, - class StringType = std::string, class BooleanType = bool, - class NumberIntegerType = std::int64_t, - class NumberUnsignedType = std::uint64_t, - class NumberFloatType = double, - template class AllocatorType = std::allocator, - template class JSONSerializer = - adl_serializer, - class BinaryType = std::vector> -class basic_json; +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. -/*! -@brief JSON Pointer - -A JSON pointer defines a string syntax for identifying a specific value -within a JSON document. It can be used with functions `at` and -`operator[]`. Furthermore, JSON pointers are the base for JSON patches. - -@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) - -@since version 2.0.0 -*/ -template -class json_pointer; - -/*! -@brief default JSON class - -This type is the default specialization of the @ref basic_json class which -uses the standard template types. +@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type @since version 1.0.0 */ -using json = basic_json<>; - -template -struct ordered_map; +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + discarded ///< discarded by the the parser callback function +}; /*! -@brief ordered JSON class +@brief comparison operator for JSON types -This type preserves the insertion order of object keys. +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string +- furthermore, each type is not smaller than itself +- discarded values are not comparable -@since version 3.9.0 +@since version 1.0.0 */ -using ordered_json = basic_json; - -} // namespace nlohmann - -#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ - - -namespace nlohmann +inline bool operator<(const value_t lhs, const value_t rhs) noexcept { -/*! -@brief detail namespace with internal helper functions - -This namespace collects functions that should not be exposed, -implementations of some @ref basic_json methods, and meta-programming helpers. - -@since version 2.1.0 -*/ -namespace detail -{ -///////////// -// helpers // -///////////// - -// Note to maintainers: -// -// Every trait in this file expects a non CV-qualified type. -// The only exceptions are in the 'aliases for detected' section -// (i.e. those of the form: decltype(T::member_function(std::declval()))) -// -// In this case, T has to be properly CV-qualified to constraint the function arguments -// (e.g. to_json(BasicJsonType&, const T&)) - -template struct is_basic_json : std::false_type {}; - -NLOHMANN_BASIC_JSON_TPL_DECLARATION -struct is_basic_json : std::true_type {}; - -////////////////////// -// json_ref helpers // -////////////////////// - -template -class json_ref; - -template -struct is_json_ref : std::false_type {}; - -template -struct is_json_ref> : std::true_type {}; - -////////////////////////// -// aliases for detected // -////////////////////////// - -template -using mapped_type_t = typename T::mapped_type; - -template -using key_type_t = typename T::key_type; - -template -using value_type_t = typename T::value_type; - -template -using difference_type_t = typename T::difference_type; - -template -using pointer_t = typename T::pointer; - -template -using reference_t = typename T::reference; - -template -using iterator_category_t = typename T::iterator_category; - -template -using iterator_t = typename T::iterator; - -template -using to_json_function = decltype(T::to_json(std::declval()...)); - -template -using from_json_function = decltype(T::from_json(std::declval()...)); - -template -using get_template_function = decltype(std::declval().template get()); - -// trait checking if JSONSerializer::from_json(json const&, udt&) exists -template -struct has_from_json : std::false_type {}; - -// trait checking if j.get is valid -// use this trait instead of std::is_constructible or std::is_convertible, -// both rely on, or make use of implicit conversions, and thus fail when T -// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) -template -struct is_getable -{ - static constexpr bool value = is_detected::value; -}; - -template -struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> -{ - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; -}; - -// This trait checks if JSONSerializer::from_json(json const&) exists -// this overload is used for non-default-constructible user-defined-types -template -struct has_non_default_from_json : std::false_type {}; - -template -struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> -{ - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; -}; - -// This trait checks if BasicJsonType::json_serializer::to_json exists -// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. -template -struct has_to_json : std::false_type {}; - -template -struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> -{ - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; -}; - - -/////////////////// -// is_ functions // -/////////////////// - -// https://en.cppreference.com/w/cpp/types/conjunction -template struct conjunction : std::true_type { }; -template struct conjunction : B1 { }; -template -struct conjunction -: std::conditional, B1>::type {}; - -// https://en.cppreference.com/w/cpp/types/negation -template struct negation : std::integral_constant < bool, !B::value > { }; - -// Reimplementation of is_constructible and is_default_constructible, due to them being broken for -// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). -// This causes compile errors in e.g. clang 3.5 or gcc 4.9. -template -struct is_default_constructible : std::is_default_constructible {}; - -template -struct is_default_constructible> - : conjunction, is_default_constructible> {}; - -template -struct is_default_constructible> - : conjunction, is_default_constructible> {}; - -template -struct is_default_constructible> - : conjunction...> {}; - -template -struct is_default_constructible> - : conjunction...> {}; - - -template -struct is_constructible : std::is_constructible {}; - -template -struct is_constructible> : is_default_constructible> {}; - -template -struct is_constructible> : is_default_constructible> {}; - -template -struct is_constructible> : is_default_constructible> {}; - -template -struct is_constructible> : is_default_constructible> {}; - - -template -struct is_iterator_traits : std::false_type {}; - -template -struct is_iterator_traits> -{ - private: - using traits = iterator_traits; - - public: - static constexpr auto value = - is_detected::value && - is_detected::value && - is_detected::value && - is_detected::value && - is_detected::value; -}; - -// The following implementation of is_complete_type is taken from -// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ -// and is written by Xiang Fan who agreed to using it in this library. - -template -struct is_complete_type : std::false_type {}; - -template -struct is_complete_type : std::true_type {}; - -template -struct is_compatible_object_type_impl : std::false_type {}; - -template -struct is_compatible_object_type_impl < - BasicJsonType, CompatibleObjectType, - enable_if_t < is_detected::value&& - is_detected::value >> -{ - using object_t = typename BasicJsonType::object_t; - - // macOS's is_constructible does not play well with nonesuch... - static constexpr bool value = - is_constructible::value && - is_constructible::value; -}; - -template -struct is_compatible_object_type - : is_compatible_object_type_impl {}; - -template -struct is_constructible_object_type_impl : std::false_type {}; - -template -struct is_constructible_object_type_impl < - BasicJsonType, ConstructibleObjectType, - enable_if_t < is_detected::value&& - is_detected::value >> -{ - using object_t = typename BasicJsonType::object_t; - - static constexpr bool value = - (is_default_constructible::value && - (std::is_move_assignable::value || - std::is_copy_assignable::value) && - (is_constructible::value && - std::is_same < - typename object_t::mapped_type, - typename ConstructibleObjectType::mapped_type >::value)) || - (has_from_json::value || - has_non_default_from_json < - BasicJsonType, - typename ConstructibleObjectType::mapped_type >::value); -}; - -template -struct is_constructible_object_type - : is_constructible_object_type_impl {}; - -template -struct is_compatible_string_type_impl : std::false_type {}; - -template -struct is_compatible_string_type_impl < - BasicJsonType, CompatibleStringType, - enable_if_t::value >> -{ - static constexpr auto value = - is_constructible::value; -}; - -template -struct is_compatible_string_type - : is_compatible_string_type_impl {}; - -template -struct is_constructible_string_type_impl : std::false_type {}; - -template -struct is_constructible_string_type_impl < - BasicJsonType, ConstructibleStringType, - enable_if_t::value >> -{ - static constexpr auto value = - is_constructible::value; -}; - -template -struct is_constructible_string_type - : is_constructible_string_type_impl {}; - -template -struct is_compatible_array_type_impl : std::false_type {}; - -template -struct is_compatible_array_type_impl < - BasicJsonType, CompatibleArrayType, - enable_if_t < is_detected::value&& - is_detected::value&& -// This is needed because json_reverse_iterator has a ::iterator type... -// Therefore it is detected as a CompatibleArrayType. -// The real fix would be to have an Iterable concept. - !is_iterator_traits < - iterator_traits>::value >> -{ - static constexpr bool value = - is_constructible::value; -}; - -template -struct is_compatible_array_type - : is_compatible_array_type_impl {}; - -template -struct is_constructible_array_type_impl : std::false_type {}; - -template -struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, - enable_if_t::value >> - : std::true_type {}; - -template -struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, - enable_if_t < !std::is_same::value&& - is_default_constructible::value&& -(std::is_move_assignable::value || - std::is_copy_assignable::value)&& -is_detected::value&& -is_detected::value&& -is_complete_type < -detected_t>::value >> -{ - static constexpr bool value = - // This is needed because json_reverse_iterator has a ::iterator type, - // furthermore, std::back_insert_iterator (and other iterators) have a - // base class `iterator`... Therefore it is detected as a - // ConstructibleArrayType. The real fix would be to have an Iterable - // concept. - !is_iterator_traits>::value && - - (std::is_same::value || - has_from_json::value || - has_non_default_from_json < - BasicJsonType, typename ConstructibleArrayType::value_type >::value); -}; - -template -struct is_constructible_array_type - : is_constructible_array_type_impl {}; - -template -struct is_compatible_integer_type_impl : std::false_type {}; - -template -struct is_compatible_integer_type_impl < - RealIntegerType, CompatibleNumberIntegerType, - enable_if_t < std::is_integral::value&& - std::is_integral::value&& - !std::is_same::value >> -{ - // is there an assert somewhere on overflows? - using RealLimits = std::numeric_limits; - using CompatibleLimits = std::numeric_limits; - - static constexpr auto value = - is_constructible::value && - CompatibleLimits::is_integer && - RealLimits::is_signed == CompatibleLimits::is_signed; -}; - -template -struct is_compatible_integer_type - : is_compatible_integer_type_impl {}; - -template -struct is_compatible_type_impl: std::false_type {}; - -template -struct is_compatible_type_impl < - BasicJsonType, CompatibleType, - enable_if_t::value >> -{ - static constexpr bool value = - has_to_json::value; -}; - -template -struct is_compatible_type - : is_compatible_type_impl {}; - -template -struct is_constructible_tuple : std::false_type {}; - -template -struct is_constructible_tuple> : conjunction...> {}; - -// a naive helper to check if a type is an ordered_map (exploits the fact that -// ordered_map inherits capacity() from std::vector) -template -struct is_ordered_map -{ - using one = char; - - struct two - { - char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */ + } }; - template static one test( decltype(&C::capacity) ) ; - template static two test(...); - - enum { value = sizeof(test(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) -}; - -// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) -template < typename T, typename U, enable_if_t < !std::is_same::value, int > = 0 > -T conditional_static_cast(U value) -{ - return static_cast(value); + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); + return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index]; +} +} } -template::value, int> = 0> -T conditional_static_cast(U value) -{ - return value; -} +// #include -} // namespace detail -} // namespace nlohmann + +#include // transform +#include // array +#include // and, not +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include + +// #include + +// #include + +// #include // #include @@ -3893,18 +978,18 @@ namespace detail template void from_json(const BasicJsonType& j, typename std::nullptr_t& n) { - if (JSON_HEDLEY_UNLIKELY(!j.is_null())) + if (JSON_UNLIKELY(not j.is_null())) { - JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); } n = nullptr; } // overloads for basic_json template parameters -template < typename BasicJsonType, typename ArithmeticType, - enable_if_t < std::is_arithmetic::value&& - !std::is_same::value, - int > = 0 > +template::value and + not std::is_same::value, + int> = 0> void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast(j)) @@ -3925,24 +1010,17 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) break; } - case value_t::null: - case value_t::object: - case value_t::array: - case value_t::string: - case value_t::boolean: - case value_t::binary: - case value_t::discarded: default: - JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); } } template void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { - if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) + if (JSON_UNLIKELY(not j.is_boolean())) { - JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); } b = *j.template get_ptr(); } @@ -3950,25 +1028,25 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) template void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) { - if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + if (JSON_UNLIKELY(not j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); } s = *j.template get_ptr(); } template < - typename BasicJsonType, typename ConstructibleStringType, + typename BasicJsonType, typename CompatibleStringType, enable_if_t < - is_constructible_string_type::value&& - !std::is_same::value, + is_compatible_string_type::value and + not std::is_same::value, int > = 0 > -void from_json(const BasicJsonType& j, ConstructibleStringType& s) +void from_json(const BasicJsonType& j, CompatibleStringType& s) { - if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + if (JSON_UNLIKELY(not j.is_string())) { - JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); } s = *j.template get_ptr(); @@ -4001,16 +1079,25 @@ void from_json(const BasicJsonType& j, EnumType& e) e = static_cast(val); } +template +void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr) +{ + if (JSON_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + arr = *j.template get_ptr(); +} + // forward_list doesn't have an insert method template::value, int> = 0> + enable_if_t::value, int> = 0> void from_json(const BasicJsonType& j, std::forward_list& l) { - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + if (JSON_UNLIKELY(not j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } - l.clear(); std::transform(j.rbegin(), j.rend(), std::front_inserter(l), [](const BasicJsonType & i) { @@ -4020,178 +1107,110 @@ void from_json(const BasicJsonType& j, std::forward_list& l) // valarray doesn't have an insert method template::value, int> = 0> + enable_if_t::value, int> = 0> void from_json(const BasicJsonType& j, std::valarray& l) { - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + if (JSON_UNLIKELY(not j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } l.resize(j.size()); - std::transform(j.begin(), j.end(), std::begin(l), - [](const BasicJsonType & elem) + std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l)); +} + +template +void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0> /*unused*/) +{ + using std::end; + + std::transform(j.begin(), j.end(), + std::inserter(arr, end(arr)), [](const BasicJsonType & i) { - return elem.template get(); + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); }); } -template -auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) --> decltype(j.template get(), void()) -{ - for (std::size_t i = 0; i < N; ++i) - { - arr[i] = j.at(i).template get(); - } -} - -template -void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) -{ - arr = *j.template get_ptr(); -} - -template -auto from_json_array_impl(const BasicJsonType& j, std::array& arr, - priority_tag<2> /*unused*/) --> decltype(j.template get(), void()) -{ - for (std::size_t i = 0; i < N; ++i) - { - arr[i] = j.at(i).template get(); - } -} - -template::value, - int> = 0> -auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) +template +auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1> /*unused*/) -> decltype( - arr.reserve(std::declval()), - j.template get(), + arr.reserve(std::declval()), void()) { using std::end; - ConstructibleArrayType ret; - ret.reserve(j.size()); + arr.reserve(j.size()); std::transform(j.begin(), j.end(), - std::inserter(ret, end(ret)), [](const BasicJsonType & i) + std::inserter(arr, end(arr)), [](const BasicJsonType & i) { // get() returns *this, this won't call a from_json // method when value_type is BasicJsonType - return i.template get(); + return i.template get(); }); - arr = std::move(ret); } -template::value, - int> = 0> -void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, - priority_tag<0> /*unused*/) +template +void from_json_array_impl(const BasicJsonType& j, std::array& arr, priority_tag<2> /*unused*/) { - using std::end; - - ConstructibleArrayType ret; - std::transform( - j.begin(), j.end(), std::inserter(ret, end(ret)), - [](const BasicJsonType & i) + for (std::size_t i = 0; i < N; ++i) { - // get() returns *this, this won't call a from_json - // method when value_type is BasicJsonType - return i.template get(); - }); - arr = std::move(ret); + arr[i] = j.at(i).template get(); + } } -template < typename BasicJsonType, typename ConstructibleArrayType, - enable_if_t < - is_constructible_array_type::value&& - !is_constructible_object_type::value&& - !is_constructible_string_type::value&& - !std::is_same::value&& - !is_basic_json::value, - int > = 0 > -auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) --> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), -j.template get(), -void()) +template < + typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < + is_compatible_array_type::value and + not std::is_same::value and + std::is_constructible < + BasicJsonType, typename CompatibleArrayType::value_type >::value, + int > = 0 > +void from_json(const BasicJsonType& j, CompatibleArrayType& arr) { - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + if (JSON_UNLIKELY(not j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); } - from_json_array_impl(j, arr, priority_tag<3> {}); + from_json_array_impl(j, arr, priority_tag<2> {}); } -template < typename BasicJsonType, typename T, std::size_t... Idx > -std::array from_json_inplace_array_impl(BasicJsonType&& j, - identity_tag> /*unused*/, index_sequence /*unused*/) +template::value, int> = 0> +void from_json(const BasicJsonType& j, CompatibleObjectType& obj) { - return { { std::forward(j).at(Idx).template get()... } }; -} - -template < typename BasicJsonType, typename T, std::size_t N > -auto from_json(BasicJsonType&& j, identity_tag> tag) --> decltype(from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {})) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + if (JSON_UNLIKELY(not j.is_object())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); } - return from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {}); -} - -template -void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) - { - JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j)); - } - - bin = *j.template get_ptr(); -} - -template::value, int> = 0> -void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_object())) - { - JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j)); - } - - ConstructibleObjectType ret; - const auto* inner_object = j.template get_ptr(); - using value_type = typename ConstructibleObjectType::value_type; + auto inner_object = j.template get_ptr(); + using value_type = typename CompatibleObjectType::value_type; std::transform( inner_object->begin(), inner_object->end(), - std::inserter(ret, ret.begin()), + std::inserter(obj, obj.begin()), [](typename BasicJsonType::object_t::value_type const & p) { - return value_type(p.first, p.second.template get()); + return value_type(p.first, p.second.template get()); }); - obj = std::move(ret); } // overload for arithmetic types, not chosen for basic_json template arguments // (BooleanType, etc..); note: Is it really necessary to provide explicit // overloads for boolean_t etc. in case of a custom BooleanType which is not // an arithmetic type? -template < typename BasicJsonType, typename ArithmeticType, - enable_if_t < - std::is_arithmetic::value&& - !std::is_same::value&& - !std::is_same::value&& - !std::is_same::value&& - !std::is_same::value, - int > = 0 > +template::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value, + int> = 0> void from_json(const BasicJsonType& j, ArithmeticType& val) { switch (static_cast(j)) @@ -4217,95 +1236,62 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) break; } - case value_t::null: - case value_t::object: - case value_t::array: - case value_t::string: - case value_t::binary: - case value_t::discarded: default: - JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); } } -template -std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) -{ - return std::make_tuple(std::forward(j).at(Idx).template get()...); -} - -template < typename BasicJsonType, class A1, class A2 > -std::pair from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) -{ - return {std::forward(j).at(0).template get(), - std::forward(j).at(1).template get()}; -} - template -void from_json_tuple_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) +void from_json(const BasicJsonType& j, std::pair& p) { - p = from_json_tuple_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); + p = {j.at(0).template get(), j.at(1).template get()}; +} + +template +void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence) +{ + t = std::make_tuple(j.at(Idx).template get::type>()...); } template -std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<2> /*unused*/) +void from_json(const BasicJsonType& j, std::tuple& t) { - return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); + from_json_tuple_impl(j, t, index_sequence_for {}); } -template -void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<3> /*unused*/) -{ - t = from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); -} - -template -auto from_json(BasicJsonType&& j, TupleRelated&& t) --> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {})) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) - { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); - } - - return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {}); -} - -template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, - typename = enable_if_t < !std::is_constructible < - typename BasicJsonType::string_t, Key >::value >> +template ::value>> void from_json(const BasicJsonType& j, std::map& m) { - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + if (JSON_UNLIKELY(not j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } - m.clear(); for (const auto& p : j) { - if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + if (JSON_UNLIKELY(not p.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); } m.emplace(p.at(0).template get(), p.at(1).template get()); } } -template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, - typename = enable_if_t < !std::is_constructible < - typename BasicJsonType::string_t, Key >::value >> +template ::value>> void from_json(const BasicJsonType& j, std::unordered_map& m) { - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + if (JSON_UNLIKELY(not j.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); } - m.clear(); for (const auto& p : j) { - if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + if (JSON_UNLIKELY(not p.is_array())) { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); } m.emplace(p.at(0).template get(), p.at(1).template get()); } @@ -4313,47 +1299,70 @@ void from_json(const BasicJsonType& j, std::unordered_map - auto operator()(const BasicJsonType& j, T&& val) const - noexcept(noexcept(from_json(j, std::forward(val)))) - -> decltype(from_json(j, std::forward(val))) + auto call(const BasicJsonType& j, T& val, priority_tag<1> /*unused*/) const + noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) { - return from_json(j, std::forward(val)); + return from_json(j, val); + } + + template + void call(const BasicJsonType& /*unused*/, T& /*unused*/, priority_tag<0> /*unused*/) const noexcept + { + static_assert(sizeof(BasicJsonType) == 0, + "could not find from_json() method in T's namespace"); +#ifdef _MSC_VER + // MSVC does not show a stacktrace for the above assert + using decayed = uncvref_t; + static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0, + "forcing MSVC stacktrace to show which T we're talking about."); +#endif + } + + public: + template + void operator()(const BasicJsonType& j, T& val) const + noexcept(noexcept(std::declval().call(j, val, priority_tag<1> {}))) + { + return call(j, val, priority_tag<1> {}); } }; -} // namespace detail +} /// namespace to hold default `from_json` function /// to see why this is required: /// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html -namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) +namespace { -constexpr const auto& from_json = detail::static_const::value; // NOLINT(misc-definitions-in-headers) -} // namespace -} // namespace nlohmann +constexpr const auto& from_json = detail::static_const::value; +} +} // #include -#include // copy +#include // or, and, not #include // begin, end -#include // string #include // tuple, get #include // is_same, is_constructible, is_floating_point, is_enum, underlying_type #include // move, forward, declval, pair #include // valarray #include // vector +// #include + +// #include + +// #include + // #include #include // size_t -#include // input_iterator_tag #include // string, to_string -#include // tuple_size, get, tuple_element -#include // move - -// #include +#include // input_iterator_tag // #include @@ -4362,114 +1371,100 @@ namespace nlohmann { namespace detail { -template -void int_to_string( string_type& target, std::size_t value ) -{ - // For ADL - using std::to_string; - target = to_string(value); -} -template class iteration_proxy_value -{ - public: - using difference_type = std::ptrdiff_t; - using value_type = iteration_proxy_value; - using pointer = value_type * ; - using reference = value_type & ; - using iterator_category = std::input_iterator_tag; - using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; - - private: - /// the iterator - IteratorType anchor; - /// an index for arrays (used to create key names) - std::size_t array_index = 0; - /// last stringified array index - mutable std::size_t array_index_last = 0; - /// a string representation of the array index - mutable string_type array_index_str = "0"; - /// an empty string (to return a reference for primitive values) - const string_type empty_str{}; - - public: - explicit iteration_proxy_value(IteratorType it) noexcept - : anchor(std::move(it)) - {} - - /// dereference operator (needed for range-based for) - iteration_proxy_value& operator*() - { - return *this; - } - - /// increment operator (needed for range-based for) - iteration_proxy_value& operator++() - { - ++anchor; - ++array_index; - - return *this; - } - - /// equality operator (needed for InputIterator) - bool operator==(const iteration_proxy_value& o) const - { - return anchor == o.anchor; - } - - /// inequality operator (needed for range-based for) - bool operator!=(const iteration_proxy_value& o) const - { - return anchor != o.anchor; - } - - /// return key of the iterator - const string_type& key() const - { - JSON_ASSERT(anchor.m_object != nullptr); - - switch (anchor.m_object->type()) - { - // use integer array index as key - case value_t::array: - { - if (array_index != array_index_last) - { - int_to_string( array_index_str, array_index ); - array_index_last = array_index; - } - return array_index_str; - } - - // use key from the object - case value_t::object: - return anchor.key(); - - // use an empty key for all primitive types - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - return empty_str; - } - } - - /// return value of the iterator - typename IteratorType::reference value() const - { - return anchor.value(); - } -}; - /// proxy class for the items() function template class iteration_proxy { private: + /// helper class for iteration + class iteration_proxy_internal + { + public: + using difference_type = std::ptrdiff_t; + using value_type = iteration_proxy_internal; + using pointer = iteration_proxy_internal*; + using reference = iteration_proxy_internal&; + using iterator_category = std::input_iterator_tag; + + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + std::size_t array_index = 0; + /// last stringified array index + mutable std::size_t array_index_last = 0; + /// a string representation of the array index + mutable std::string array_index_str = "0"; + /// an empty string (to return a reference for primitive values) + const std::string empty_str = ""; + + public: + explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it) {} + + iteration_proxy_internal(const iteration_proxy_internal&) = default; + iteration_proxy_internal& operator=(const iteration_proxy_internal&) = default; + + /// dereference operator (needed for range-based for) + iteration_proxy_internal& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_internal& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// equality operator (needed for InputIterator) + bool operator==(const iteration_proxy_internal& o) const noexcept + { + return anchor == o.anchor; + } + + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_internal& o) const noexcept + { + return anchor != o.anchor; + } + + /// return key of the iterator + const std::string& key() const + { + assert(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + { + if (array_index != array_index_last) + { + array_index_str = std::to_string(array_index); + array_index_last = array_index; + } + return array_index_str; + } + + // use key from the object + case value_t::object: + return anchor.key(); + + // use an empty key for all primitive types + default: + return empty_str; + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } + }; + /// the container to iterate typename IteratorType::reference container; @@ -4479,69 +1474,19 @@ template class iteration_proxy : container(cont) {} /// return iterator begin (needed for range-based for) - iteration_proxy_value begin() noexcept + iteration_proxy_internal begin() noexcept { - return iteration_proxy_value(container.begin()); + return iteration_proxy_internal(container.begin()); } /// return iterator end (needed for range-based for) - iteration_proxy_value end() noexcept + iteration_proxy_internal end() noexcept { - return iteration_proxy_value(container.end()); + return iteration_proxy_internal(container.end()); } }; -// Structured Bindings Support -// For further reference see https://blog.tartanllama.xyz/structured-bindings/ -// And see https://github.com/nlohmann/json/pull/1391 -template = 0> -auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) -{ - return i.key(); } -// Structured Bindings Support -// For further reference see https://blog.tartanllama.xyz/structured-bindings/ -// And see https://github.com/nlohmann/json/pull/1391 -template = 0> -auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) -{ - return i.value(); } -} // namespace detail -} // namespace nlohmann - -// The Addition to the STD Namespace is required to add -// Structured Bindings Support to the iteration_proxy_value class -// For further reference see https://blog.tartanllama.xyz/structured-bindings/ -// And see https://github.com/nlohmann/json/pull/1391 -namespace std -{ -#if defined(__clang__) - // Fix: https://github.com/nlohmann/json/issues/1401 - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wmismatched-tags" -#endif -template -class tuple_size<::nlohmann::detail::iteration_proxy_value> - : public std::integral_constant {}; - -template -class tuple_element> -{ - public: - using type = decltype( - get(std::declval < - ::nlohmann::detail::iteration_proxy_value> ())); -}; -#if defined(__clang__) - #pragma clang diagnostic pop -#endif -} // namespace std - -// #include - -// #include - -// #include namespace nlohmann @@ -4552,13 +1497,6 @@ namespace detail // constructors // ////////////////// -/* - * Note all external_constructor<>::construct functions need to call - * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an - * allocated value (e.g., a string). See bug issue - * https://github.com/nlohmann/json/issues/2865 for more information. - */ - template struct external_constructor; template<> @@ -4567,7 +1505,6 @@ struct external_constructor template static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept { - j.m_value.destroy(j.m_type); j.m_type = value_t::boolean; j.m_value = b; j.assert_invariant(); @@ -4580,7 +1517,6 @@ struct external_constructor template static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) { - j.m_value.destroy(j.m_type); j.m_type = value_t::string; j.m_value = s; j.assert_invariant(); @@ -4589,53 +1525,28 @@ struct external_constructor template static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) { - j.m_value.destroy(j.m_type); j.m_type = value_t::string; j.m_value = std::move(s); j.assert_invariant(); } - template < typename BasicJsonType, typename CompatibleStringType, - enable_if_t < !std::is_same::value, - int > = 0 > + template::value, + int> = 0> static void construct(BasicJsonType& j, const CompatibleStringType& str) { - j.m_value.destroy(j.m_type); j.m_type = value_t::string; j.m_value.string = j.template create(str); j.assert_invariant(); } }; -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::binary; - j.m_value = typename BasicJsonType::binary_t(b); - j.assert_invariant(); - } - - template - static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::binary; - j.m_value = typename BasicJsonType::binary_t(std::move(b)); - j.assert_invariant(); - } -}; - template<> struct external_constructor { template static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept { - j.m_value.destroy(j.m_type); j.m_type = value_t::number_float; j.m_value = val; j.assert_invariant(); @@ -4648,7 +1559,6 @@ struct external_constructor template static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept { - j.m_value.destroy(j.m_type); j.m_type = value_t::number_unsigned; j.m_value = val; j.assert_invariant(); @@ -4661,7 +1571,6 @@ struct external_constructor template static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept { - j.m_value.destroy(j.m_type); j.m_type = value_t::number_integer; j.m_value = val; j.assert_invariant(); @@ -4674,49 +1583,40 @@ struct external_constructor template static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) { - j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value = arr; - j.set_parents(); j.assert_invariant(); } template static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) { - j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value = std::move(arr); - j.set_parents(); j.assert_invariant(); } - template < typename BasicJsonType, typename CompatibleArrayType, - enable_if_t < !std::is_same::value, - int > = 0 > + template::value, + int> = 0> static void construct(BasicJsonType& j, const CompatibleArrayType& arr) { using std::begin; using std::end; - - j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value.array = j.template create(begin(arr), end(arr)); - j.set_parents(); j.assert_invariant(); } template static void construct(BasicJsonType& j, const std::vector& arr) { - j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value = value_t::array; j.m_value.array->reserve(arr.size()); for (const bool x : arr) { j.m_value.array->push_back(x); - j.set_parent(j.m_value.array->back()); } j.assert_invariant(); } @@ -4725,15 +1625,10 @@ struct external_constructor enable_if_t::value, int> = 0> static void construct(BasicJsonType& j, const std::valarray& arr) { - j.m_value.destroy(j.m_type); j.m_type = value_t::array; j.m_value = value_t::array; j.m_value.array->resize(arr.size()); - if (arr.size() > 0) - { - std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); - } - j.set_parents(); + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); j.assert_invariant(); } }; @@ -4744,34 +1639,28 @@ struct external_constructor template static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) { - j.m_value.destroy(j.m_type); j.m_type = value_t::object; j.m_value = obj; - j.set_parents(); j.assert_invariant(); } template static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) { - j.m_value.destroy(j.m_type); j.m_type = value_t::object; j.m_value = std::move(obj); - j.set_parents(); j.assert_invariant(); } - template < typename BasicJsonType, typename CompatibleObjectType, - enable_if_t < !std::is_same::value, int > = 0 > + template::value, int> = 0> static void construct(BasicJsonType& j, const CompatibleObjectType& obj) { using std::begin; using std::end; - j.m_value.destroy(j.m_type); j.m_type = value_t::object; j.m_value.object = j.template create(begin(obj), end(obj)); - j.set_parents(); j.assert_invariant(); } }; @@ -4835,25 +1724,15 @@ void to_json(BasicJsonType& j, const std::vector& e) external_constructor::construct(j, e); } -template < typename BasicJsonType, typename CompatibleArrayType, - enable_if_t < is_compatible_array_type::value&& - !is_compatible_object_type::value&& - !is_compatible_string_type::value&& - !std::is_same::value&& - !is_basic_json::value, - int > = 0 > +template::value or + std::is_same::value, + int> = 0> void to_json(BasicJsonType& j, const CompatibleArrayType& arr) { external_constructor::construct(j, arr); } -template -void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) -{ - external_constructor::construct(j, bin); -} - template::value, int> = 0> void to_json(BasicJsonType& j, const std::valarray& arr) @@ -4867,8 +1746,8 @@ void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) external_constructor::construct(j, std::move(arr)); } -template < typename BasicJsonType, typename CompatibleObjectType, - enable_if_t < is_compatible_object_type::value&& !is_basic_json::value, int > = 0 > +template::value, int> = 0> void to_json(BasicJsonType& j, const CompatibleObjectType& obj) { external_constructor::construct(j, obj); @@ -4880,458 +1759,87 @@ void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) external_constructor::construct(j, std::move(obj)); } -template < - typename BasicJsonType, typename T, std::size_t N, - enable_if_t < !std::is_constructible::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - int > = 0 > -void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +template::value, int> = 0> +void to_json(BasicJsonType& j, T (&arr)[N]) { external_constructor::construct(j, arr); } -template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible::value&& std::is_constructible::value, int > = 0 > -void to_json(BasicJsonType& j, const std::pair& p) +template +void to_json(BasicJsonType& j, const std::pair& p) { - j = { p.first, p.second }; + j = {p.first, p.second}; } // for https://github.com/nlohmann/json/pull/1134 template>::value, int> = 0> -void to_json(BasicJsonType& j, const T& b) + enable_if_t::iteration_proxy_internal>::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept { - j = { {b.key(), b.value()} }; + j = {{b.key(), b.value()}}; } template -void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) +void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence) { - j = { std::get(t)... }; + j = {std::get(t)...}; } -template::value, int > = 0> -void to_json(BasicJsonType& j, const T& t) +template +void to_json(BasicJsonType& j, const std::tuple& t) { - to_json_tuple_impl(j, t, make_index_sequence::value> {}); + to_json_tuple_impl(j, t, index_sequence_for {}); } struct to_json_fn { + private: template - auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward(val)))) + auto call(BasicJsonType& j, T&& val, priority_tag<1> /*unused*/) const noexcept(noexcept(to_json(j, std::forward(val)))) -> decltype(to_json(j, std::forward(val)), void()) { return to_json(j, std::forward(val)); } + + template + void call(BasicJsonType& /*unused*/, T&& /*unused*/, priority_tag<0> /*unused*/) const noexcept + { + static_assert(sizeof(BasicJsonType) == 0, + "could not find to_json() method in T's namespace"); + +#ifdef _MSC_VER + // MSVC does not show a stacktrace for the above assert + using decayed = uncvref_t; + static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0, + "forcing MSVC stacktrace to show which T we're talking about."); +#endif + } + + public: + template + void operator()(BasicJsonType& j, T&& val) const + noexcept(noexcept(std::declval().call(j, std::forward(val), priority_tag<1> {}))) + { + return call(j, std::forward(val), priority_tag<1> {}); + } }; -} // namespace detail +} /// namespace to hold default `to_json` function -/// to see why this is required: -/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html -namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) +namespace { -constexpr const auto& to_json = detail::static_const::value; // NOLINT(misc-definitions-in-headers) -} // namespace -} // namespace nlohmann - -// #include - -// #include - - -namespace nlohmann -{ - -template -struct adl_serializer -{ - /*! - @brief convert a JSON value to any value type - - This function is usually called by the `get()` function of the - @ref basic_json class (either explicit or via conversion operators). - - @note This function is chosen for default-constructible value types. - - @param[in] j JSON value to read from - @param[in,out] val value to write to - */ - template - static auto from_json(BasicJsonType && j, TargetType& val) noexcept( - noexcept(::nlohmann::from_json(std::forward(j), val))) - -> decltype(::nlohmann::from_json(std::forward(j), val), void()) - { - ::nlohmann::from_json(std::forward(j), val); - } - - /*! - @brief convert a JSON value to any value type - - This function is usually called by the `get()` function of the - @ref basic_json class (either explicit or via conversion operators). - - @note This function is chosen for value types which are not default-constructible. - - @param[in] j JSON value to read from - - @return copy of the JSON value, converted to @a ValueType - */ - template - static auto from_json(BasicJsonType && j) noexcept( - noexcept(::nlohmann::from_json(std::forward(j), detail::identity_tag {}))) - -> decltype(::nlohmann::from_json(std::forward(j), detail::identity_tag {})) - { - return ::nlohmann::from_json(std::forward(j), detail::identity_tag {}); - } - - /*! - @brief convert any value type to a JSON value - - This function is usually called by the constructors of the @ref basic_json - class. - - @param[in,out] j JSON value to write to - @param[in] val value to read from - */ - template - static auto to_json(BasicJsonType& j, TargetType && val) noexcept( - noexcept(::nlohmann::to_json(j, std::forward(val)))) - -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) - { - ::nlohmann::to_json(j, std::forward(val)); - } -}; -} // namespace nlohmann - -// #include - - -#include // uint8_t, uint64_t -#include // tie -#include // move - -namespace nlohmann -{ - -/*! -@brief an internal type for a backed binary type - -This type extends the template parameter @a BinaryType provided to `basic_json` -with a subtype used by BSON and MessagePack. This type exists so that the user -does not have to specify a type themselves with a specific naming scheme in -order to override the binary type. - -@tparam BinaryType container to store bytes (`std::vector` by - default) - -@since version 3.8.0; changed type of subtypes to std::uint64_t in 3.10.0. -*/ -template -class byte_container_with_subtype : public BinaryType -{ - public: - /// the type of the underlying container - using container_type = BinaryType; - /// the type of the subtype - using subtype_type = std::uint64_t; - - byte_container_with_subtype() noexcept(noexcept(container_type())) - : container_type() - {} - - byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) - : container_type(b) - {} - - byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) - : container_type(std::move(b)) - {} - - byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b))) - : container_type(b) - , m_subtype(subtype_) - , m_has_subtype(true) - {} - - byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b)))) - : container_type(std::move(b)) - , m_subtype(subtype_) - , m_has_subtype(true) - {} - - bool operator==(const byte_container_with_subtype& rhs) const - { - return std::tie(static_cast(*this), m_subtype, m_has_subtype) == - std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); - } - - bool operator!=(const byte_container_with_subtype& rhs) const - { - return !(rhs == *this); - } - - /*! - @brief sets the binary subtype - - Sets the binary subtype of the value, also flags a binary JSON value as - having a subtype, which has implications for serialization. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa see @ref subtype() -- return the binary subtype - @sa see @ref clear_subtype() -- clears the binary subtype - @sa see @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - void set_subtype(subtype_type subtype_) noexcept - { - m_subtype = subtype_; - m_has_subtype = true; - } - - /*! - @brief return the binary subtype - - Returns the numerical subtype of the value if it has a subtype. If it does - not have a subtype, this function will return subtype_type(-1) as a sentinel - value. - - @return the numerical subtype of the binary value - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa see @ref set_subtype() -- sets the binary subtype - @sa see @ref clear_subtype() -- clears the binary subtype - @sa see @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0; fixed return value to properly return - subtype_type(-1) as documented in version 3.10.0 - */ - constexpr subtype_type subtype() const noexcept - { - return m_has_subtype ? m_subtype : subtype_type(-1); - } - - /*! - @brief return whether the value has a subtype - - @return whether the value has a subtype - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa see @ref subtype() -- return the binary subtype - @sa see @ref set_subtype() -- sets the binary subtype - @sa see @ref clear_subtype() -- clears the binary subtype - - @since version 3.8.0 - */ - constexpr bool has_subtype() const noexcept - { - return m_has_subtype; - } - - /*! - @brief clears the binary subtype - - Clears the binary subtype and flags the value as not having a subtype, which - has implications for serialization; for instance MessagePack will prefer the - bin family over the ext family. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa see @ref subtype() -- return the binary subtype - @sa see @ref set_subtype() -- sets the binary subtype - @sa see @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - void clear_subtype() noexcept - { - m_subtype = 0; - m_has_subtype = false; - } - - private: - subtype_type m_subtype = 0; - bool m_has_subtype = false; -}; - -} // namespace nlohmann - -// #include - -// #include - -// #include - -// #include - - -#include // uint8_t -#include // size_t -#include // hash - -// #include - -// #include - - -namespace nlohmann -{ -namespace detail -{ - -// boost::hash_combine -inline std::size_t combine(std::size_t seed, std::size_t h) noexcept -{ - seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); - return seed; +constexpr const auto& to_json = detail::static_const::value; } - -/*! -@brief hash a JSON value - -The hash function tries to rely on std::hash where possible. Furthermore, the -type of the JSON value is taken into account to have different hash values for -null, 0, 0U, and false, etc. - -@tparam BasicJsonType basic_json specialization -@param j JSON value to hash -@return hash value of j -*/ -template -std::size_t hash(const BasicJsonType& j) -{ - using string_t = typename BasicJsonType::string_t; - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - - const auto type = static_cast(j.type()); - switch (j.type()) - { - case BasicJsonType::value_t::null: - case BasicJsonType::value_t::discarded: - { - return combine(type, 0); - } - - case BasicJsonType::value_t::object: - { - auto seed = combine(type, j.size()); - for (const auto& element : j.items()) - { - const auto h = std::hash {}(element.key()); - seed = combine(seed, h); - seed = combine(seed, hash(element.value())); - } - return seed; - } - - case BasicJsonType::value_t::array: - { - auto seed = combine(type, j.size()); - for (const auto& element : j) - { - seed = combine(seed, hash(element)); - } - return seed; - } - - case BasicJsonType::value_t::string: - { - const auto h = std::hash {}(j.template get_ref()); - return combine(type, h); - } - - case BasicJsonType::value_t::boolean: - { - const auto h = std::hash {}(j.template get()); - return combine(type, h); - } - - case BasicJsonType::value_t::number_integer: - { - const auto h = std::hash {}(j.template get()); - return combine(type, h); - } - - case BasicJsonType::value_t::number_unsigned: - { - const auto h = std::hash {}(j.template get()); - return combine(type, h); - } - - case BasicJsonType::value_t::number_float: - { - const auto h = std::hash {}(j.template get()); - return combine(type, h); - } - - case BasicJsonType::value_t::binary: - { - auto seed = combine(type, j.get_binary().size()); - const auto h = std::hash {}(j.get_binary().has_subtype()); - seed = combine(seed, h); - seed = combine(seed, static_cast(j.get_binary().subtype())); - for (const auto byte : j.get_binary()) - { - seed = combine(seed, std::hash {}(byte)); - } - return seed; - } - - default: // LCOV_EXCL_LINE - JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE - return 0; // LCOV_EXCL_LINE - } } -} // namespace detail -} // namespace nlohmann - -// #include - - -#include // generate_n -#include // array -#include // ldexp -#include // size_t -#include // uint8_t, uint16_t, uint32_t, uint64_t -#include // snprintf -#include // memcpy -#include // back_inserter -#include // numeric_limits -#include // char_traits, string -#include // make_pair, move -#include // vector - -// #include - // #include -#include // array +#include // assert #include // size_t #include // strlen +#include // istream #include // begin, end, iterator_traits, random_access_iterator_tag, distance, next #include // shared_ptr, make_shared, addressof #include // accumulate @@ -5339,13 +1847,6 @@ std::size_t hash(const BasicJsonType& j) #include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer #include // pair, declval -#ifndef JSON_NO_IO - #include // FILE * - #include // istream -#endif // JSON_NO_IO - -// #include - // #include @@ -5354,44 +1855,32 @@ namespace nlohmann namespace detail { /// the supported input formats -enum class input_format_t { json, cbor, msgpack, ubjson, bson }; +enum class input_format_t { json, cbor, msgpack, ubjson }; //////////////////// // input adapters // //////////////////// -#ifndef JSON_NO_IO /*! -Input adapter for stdio file access. This adapter read only 1 byte and do not use any - buffer. This adapter is a very low level adapter. +@brief abstract input adapter interface + +Produces a stream of std::char_traits::int_type characters from a +std::istream, a buffer, or some other input type. Accepts the return of +exactly one non-EOF character for future input. The int_type characters +returned consist of all valid char values as positive values (typically +unsigned char), plus an EOF value outside that range, specified by the value +of the function std::char_traits::eof(). This value is typically -1, but +could be any arbitrary value which is not a valid char value. */ -class file_input_adapter +struct input_adapter_protocol { - public: - using char_type = char; - - JSON_HEDLEY_NON_NULL(2) - explicit file_input_adapter(std::FILE* f) noexcept - : m_file(f) - {} - - // make class move-only - file_input_adapter(const file_input_adapter&) = delete; - file_input_adapter(file_input_adapter&&) noexcept = default; - file_input_adapter& operator=(const file_input_adapter&) = delete; - file_input_adapter& operator=(file_input_adapter&&) = delete; - ~file_input_adapter() = default; - - std::char_traits::int_type get_character() noexcept - { - return std::fgetc(m_file); - } - - private: - /// the file pointer to read from - std::FILE* m_file; + /// get a character [0,255] or std::char_traits::eof(). + virtual std::char_traits::int_type get_character() = 0; + virtual ~input_adapter_protocol() = default; }; +/// a type to simplify interfaces +using input_adapter_t = std::shared_ptr; /*! Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at @@ -5402,169 +1891,103 @@ characters following those used in parsing the JSON input. Clears the std::istream flags; any input errors (e.g., EOF) will be detected by the first subsequent call for input from the std::istream. */ -class input_stream_adapter +class input_stream_adapter : public input_adapter_protocol { public: - using char_type = char; - - ~input_stream_adapter() + ~input_stream_adapter() override { // clear stream flags; we use underlying streambuf I/O, do not - // maintain ifstream flags, except eof - if (is != nullptr) - { - is->clear(is->rdstate() & std::ios::eofbit); - } + // maintain ifstream flags + is.clear(); } explicit input_stream_adapter(std::istream& i) - : is(&i), sb(i.rdbuf()) + : is(i), sb(*i.rdbuf()) {} // delete because of pointer members input_stream_adapter(const input_stream_adapter&) = delete; input_stream_adapter& operator=(input_stream_adapter&) = delete; - input_stream_adapter& operator=(input_stream_adapter&&) = delete; - - input_stream_adapter(input_stream_adapter&& rhs) noexcept - : is(rhs.is), sb(rhs.sb) - { - rhs.is = nullptr; - rhs.sb = nullptr; - } // std::istream/std::streambuf use std::char_traits::to_int_type, to // ensure that std::char_traits::eof() and the character 0xFF do not // end up as the same value, eg. 0xFFFFFFFF. - std::char_traits::int_type get_character() + std::char_traits::int_type get_character() override { - auto res = sb->sbumpc(); - // set eof manually, as we don't use the istream interface. - if (JSON_HEDLEY_UNLIKELY(res == std::char_traits::eof())) - { - is->clear(is->rdstate() | std::ios::eofbit); - } - return res; + return sb.sbumpc(); } private: /// the associated input stream - std::istream* is = nullptr; - std::streambuf* sb = nullptr; + std::istream& is; + std::streambuf& sb; }; -#endif // JSON_NO_IO -// General-purpose iterator-based adapter. It might not be as fast as -// theoretically possible for some containers, but it is extremely versatile. -template -class iterator_input_adapter +/// input adapter for buffer input +class input_buffer_adapter : public input_adapter_protocol { public: - using char_type = typename std::iterator_traits::value_type; - - iterator_input_adapter(IteratorType first, IteratorType last) - : current(std::move(first)), end(std::move(last)) + input_buffer_adapter(const char* b, const std::size_t l) + : cursor(b), limit(b + l) {} - typename std::char_traits::int_type get_character() + // delete because of pointer members + input_buffer_adapter(const input_buffer_adapter&) = delete; + input_buffer_adapter& operator=(input_buffer_adapter&) = delete; + + std::char_traits::int_type get_character() noexcept override { - if (JSON_HEDLEY_LIKELY(current != end)) + if (JSON_LIKELY(cursor < limit)) { - auto result = std::char_traits::to_int_type(*current); - std::advance(current, 1); - return result; + return std::char_traits::to_int_type(*(cursor++)); } - return std::char_traits::eof(); + return std::char_traits::eof(); } private: - IteratorType current; - IteratorType end; - - template - friend struct wide_string_input_helper; - - bool empty() const - { - return current == end; - } + /// pointer to the current character + const char* cursor; + /// pointer past the last character + const char* const limit; }; - -template -struct wide_string_input_helper; - -template -struct wide_string_input_helper +template +class wide_string_input_adapter : public input_adapter_protocol { - // UTF-32 - static void fill_buffer(BaseInputAdapter& input, - std::array::int_type, 4>& utf8_bytes, - size_t& utf8_bytes_index, - size_t& utf8_bytes_filled) + public: + explicit wide_string_input_adapter(const WideStringType& w) : str(w) {} + + std::char_traits::int_type get_character() noexcept override { - utf8_bytes_index = 0; - - if (JSON_HEDLEY_UNLIKELY(input.empty())) + // check if buffer needs to be filled + if (utf8_bytes_index == utf8_bytes_filled) { - utf8_bytes[0] = std::char_traits::eof(); - utf8_bytes_filled = 1; - } - else - { - // get the current character - const auto wc = input.get_character(); - - // UTF-32 to UTF-8 encoding - if (wc < 0x80) + if (sizeof(typename WideStringType::value_type) == 2) { - utf8_bytes[0] = static_cast::int_type>(wc); - utf8_bytes_filled = 1; - } - else if (wc <= 0x7FF) - { - utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u) & 0x1Fu)); - utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); - utf8_bytes_filled = 2; - } - else if (wc <= 0xFFFF) - { - utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u) & 0x0Fu)); - utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); - utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); - utf8_bytes_filled = 3; - } - else if (wc <= 0x10FFFF) - { - utf8_bytes[0] = static_cast::int_type>(0xF0u | ((static_cast(wc) >> 18u) & 0x07u)); - utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 12u) & 0x3Fu)); - utf8_bytes[2] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); - utf8_bytes[3] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); - utf8_bytes_filled = 4; + fill_buffer_utf16(); } else { - // unknown character - utf8_bytes[0] = static_cast::int_type>(wc); - utf8_bytes_filled = 1; + fill_buffer_utf32(); } - } - } -}; -template -struct wide_string_input_helper -{ - // UTF-16 - static void fill_buffer(BaseInputAdapter& input, - std::array::int_type, 4>& utf8_bytes, - size_t& utf8_bytes_index, - size_t& utf8_bytes_filled) + assert(utf8_bytes_filled > 0); + assert(utf8_bytes_index == 0); + } + + // use buffer + assert(utf8_bytes_filled > 0); + assert(utf8_bytes_index < utf8_bytes_filled); + return utf8_bytes[utf8_bytes_index++]; + } + + private: + void fill_buffer_utf16() { utf8_bytes_index = 0; - if (JSON_HEDLEY_UNLIKELY(input.empty())) + if (current_wchar == str.size()) { utf8_bytes[0] = std::char_traits::eof(); utf8_bytes_filled = 1; @@ -5572,84 +1995,106 @@ struct wide_string_input_helper else { // get the current character - const auto wc = input.get_character(); + const int wc = static_cast(str[current_wchar++]); // UTF-16 to UTF-8 encoding if (wc < 0x80) { - utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes[0] = wc; utf8_bytes_filled = 1; } else if (wc <= 0x7FF) { - utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u))); - utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes[0] = 0xC0 | ((wc >> 6)); + utf8_bytes[1] = 0x80 | (wc & 0x3F); utf8_bytes_filled = 2; } - else if (0xD800 > wc || wc >= 0xE000) + else if (0xD800 > wc or wc >= 0xE000) { - utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u))); - utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); - utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes[0] = 0xE0 | ((wc >> 12)); + utf8_bytes[1] = 0x80 | ((wc >> 6) & 0x3F); + utf8_bytes[2] = 0x80 | (wc & 0x3F); utf8_bytes_filled = 3; } else { - if (JSON_HEDLEY_UNLIKELY(!input.empty())) + if (current_wchar < str.size()) { - const auto wc2 = static_cast(input.get_character()); - const auto charcode = 0x10000u + (((static_cast(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); - utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); - utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); - utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); - utf8_bytes[3] = static_cast::int_type>(0x80u | (charcode & 0x3Fu)); + const int wc2 = static_cast(str[current_wchar++]); + const int charcode = 0x10000 + (((wc & 0x3FF) << 10) | (wc2 & 0x3FF)); + utf8_bytes[0] = 0xf0 | (charcode >> 18); + utf8_bytes[1] = 0x80 | ((charcode >> 12) & 0x3F); + utf8_bytes[2] = 0x80 | ((charcode >> 6) & 0x3F); + utf8_bytes[3] = 0x80 | (charcode & 0x3F); utf8_bytes_filled = 4; } else { - utf8_bytes[0] = static_cast::int_type>(wc); + // unknown character + ++current_wchar; + utf8_bytes[0] = wc; utf8_bytes_filled = 1; } } } } -}; -// Wraps another input apdater to convert wide character types into individual bytes. -template -class wide_string_input_adapter -{ - public: - using char_type = char; - - wide_string_input_adapter(BaseInputAdapter base) - : base_adapter(base) {} - - typename std::char_traits::int_type get_character() noexcept + void fill_buffer_utf32() { - // check if buffer needs to be filled - if (utf8_bytes_index == utf8_bytes_filled) + utf8_bytes_index = 0; + + if (current_wchar == str.size()) { - fill_buffer(); - - JSON_ASSERT(utf8_bytes_filled > 0); - JSON_ASSERT(utf8_bytes_index == 0); + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; } + else + { + // get the current character + const int wc = static_cast(str[current_wchar++]); - // use buffer - JSON_ASSERT(utf8_bytes_filled > 0); - JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled); - return utf8_bytes[utf8_bytes_index++]; + // UTF-32 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = wc; + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = 0xC0 | ((wc >> 6) & 0x1F); + utf8_bytes[1] = 0x80 | (wc & 0x3F); + utf8_bytes_filled = 2; + } + else if (wc <= 0xFFFF) + { + utf8_bytes[0] = 0xE0 | ((wc >> 12) & 0x0F); + utf8_bytes[1] = 0x80 | ((wc >> 6) & 0x3F); + utf8_bytes[2] = 0x80 | (wc & 0x3F); + utf8_bytes_filled = 3; + } + else if (wc <= 0x10FFFF) + { + utf8_bytes[0] = 0xF0 | ((wc >> 18 ) & 0x07); + utf8_bytes[1] = 0x80 | ((wc >> 12) & 0x3F); + utf8_bytes[2] = 0x80 | ((wc >> 6) & 0x3F); + utf8_bytes[3] = 0x80 | (wc & 0x3F); + utf8_bytes_filled = 4; + } + else + { + // unknown character + utf8_bytes[0] = wc; + utf8_bytes_filled = 1; + } + } } private: - BaseInputAdapter base_adapter; + /// the wstring to process + const WideStringType& str; - template - void fill_buffer() - { - wide_string_input_helper::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); - } + /// index of the current wchar in str + std::size_t current_wchar = 0; /// a buffer for UTF-8 bytes std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; @@ -5660,893 +2105,126 @@ class wide_string_input_adapter std::size_t utf8_bytes_filled = 0; }; - -template -struct iterator_input_adapter_factory -{ - using iterator_type = IteratorType; - using char_type = typename std::iterator_traits::value_type; - using adapter_type = iterator_input_adapter; - - static adapter_type create(IteratorType first, IteratorType last) - { - return adapter_type(std::move(first), std::move(last)); - } -}; - -template -struct is_iterator_of_multibyte -{ - using value_type = typename std::iterator_traits::value_type; - enum - { - value = sizeof(value_type) > 1 - }; -}; - -template -struct iterator_input_adapter_factory::value>> -{ - using iterator_type = IteratorType; - using char_type = typename std::iterator_traits::value_type; - using base_adapter_type = iterator_input_adapter; - using adapter_type = wide_string_input_adapter; - - static adapter_type create(IteratorType first, IteratorType last) - { - return adapter_type(base_adapter_type(std::move(first), std::move(last))); - } -}; - -// General purpose iterator-based input -template -typename iterator_input_adapter_factory::adapter_type input_adapter(IteratorType first, IteratorType last) -{ - using factory_type = iterator_input_adapter_factory; - return factory_type::create(first, last); -} - -// Convenience shorthand from container to iterator -// Enables ADL on begin(container) and end(container) -// Encloses the using declarations in namespace for not to leak them to outside scope - -namespace container_input_adapter_factory_impl -{ - -using std::begin; -using std::end; - -template -struct container_input_adapter_factory {}; - -template -struct container_input_adapter_factory< ContainerType, - void_t()), end(std::declval()))>> - { - using adapter_type = decltype(input_adapter(begin(std::declval()), end(std::declval()))); - - static adapter_type create(const ContainerType& container) -{ - return input_adapter(begin(container), end(container)); -} - }; - -} // namespace container_input_adapter_factory_impl - -template -typename container_input_adapter_factory_impl::container_input_adapter_factory::adapter_type input_adapter(const ContainerType& container) -{ - return container_input_adapter_factory_impl::container_input_adapter_factory::create(container); -} - -#ifndef JSON_NO_IO -// Special cases with fast paths -inline file_input_adapter input_adapter(std::FILE* file) -{ - return file_input_adapter(file); -} - -inline input_stream_adapter input_adapter(std::istream& stream) -{ - return input_stream_adapter(stream); -} - -inline input_stream_adapter input_adapter(std::istream&& stream) -{ - return input_stream_adapter(stream); -} -#endif // JSON_NO_IO - -using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval())); - -// Null-delimited strings, and the like. -template < typename CharT, - typename std::enable_if < - std::is_pointer::value&& - !std::is_array::value&& - std::is_integral::type>::value&& - sizeof(typename std::remove_pointer::type) == 1, - int >::type = 0 > -contiguous_bytes_input_adapter input_adapter(CharT b) -{ - auto length = std::strlen(reinterpret_cast(b)); - const auto* ptr = reinterpret_cast(b); - return input_adapter(ptr, ptr + length); -} - -template -auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) -{ - return input_adapter(array, array + N); -} - -// This class only handles inputs of input_buffer_adapter type. -// It's required so that expressions like {ptr, len} can be implicitely casted -// to the correct adapter. -class span_input_adapter +class input_adapter { public: - template < typename CharT, - typename std::enable_if < - std::is_pointer::value&& - std::is_integral::type>::value&& - sizeof(typename std::remove_pointer::type) == 1, - int >::type = 0 > - span_input_adapter(CharT b, std::size_t l) - : ia(reinterpret_cast(b), reinterpret_cast(b) + l) {} + // native support + /// input adapter for input stream + input_adapter(std::istream& i) + : ia(std::make_shared(i)) {} + + /// input adapter for input stream + input_adapter(std::istream&& i) + : ia(std::make_shared(i)) {} + + input_adapter(const std::wstring& ws) + : ia(std::make_shared>(ws)) {} + + input_adapter(const std::u16string& ws) + : ia(std::make_shared>(ws)) {} + + input_adapter(const std::u32string& ws) + : ia(std::make_shared>(ws)) {} + + /// input adapter for buffer + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> + input_adapter(CharT b, std::size_t l) + : ia(std::make_shared(reinterpret_cast(b), l)) {} + + // derived support + + /// input adapter for string literal + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> + input_adapter(CharT b) + : input_adapter(reinterpret_cast(b), + std::strlen(reinterpret_cast(b))) {} + + /// input adapter for iterator range with contiguous storage template::iterator_category, std::random_access_iterator_tag>::value, + std::is_same::iterator_category, std::random_access_iterator_tag>::value, int>::type = 0> - span_input_adapter(IteratorType first, IteratorType last) - : ia(input_adapter(first, last)) {} - - contiguous_bytes_input_adapter&& get() + input_adapter(IteratorType first, IteratorType last) { - return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg) + // assertion to check that the iterator range is indeed contiguous, + // see http://stackoverflow.com/a/35008842/266378 for more discussion + assert(std::accumulate( + first, last, std::pair(true, 0), + [&first](std::pair res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first); + + // assertion to check that each element is 1 byte long + static_assert( + sizeof(typename std::iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + const auto len = static_cast(std::distance(first, last)); + if (JSON_LIKELY(len > 0)) + { + // there is at least one element: use the address of first + ia = std::make_shared(reinterpret_cast(&(*first)), len); + } + else + { + // the address of first cannot be used: use nullptr + ia = std::make_shared(nullptr, len); + } + } + + /// input adapter for array + template + input_adapter(T (&array)[N]) + : input_adapter(std::begin(array), std::end(array)) {} + + /// input adapter for contiguous container + template::value and + std::is_base_of()))>::iterator_category>::value, + int>::type = 0> + input_adapter(const ContiguousContainer& c) + : input_adapter(std::begin(c), std::end(c)) {} + + operator input_adapter_t() + { + return ia; } private: - contiguous_bytes_input_adapter ia; + /// the actual adapter + input_adapter_t ia = nullptr; }; -} // namespace detail -} // namespace nlohmann - -// #include - - -#include -#include // string -#include // move -#include // vector - -// #include - -// #include - - -namespace nlohmann -{ - -/*! -@brief SAX interface - -This class describes the SAX interface used by @ref nlohmann::json::sax_parse. -Each function is called in different situations while the input is parsed. The -boolean return value informs the parser whether to continue processing the -input. -*/ -template -struct json_sax -{ - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; - - /*! - @brief a null value was read - @return whether parsing should proceed - */ - virtual bool null() = 0; - - /*! - @brief a boolean value was read - @param[in] val boolean value - @return whether parsing should proceed - */ - virtual bool boolean(bool val) = 0; - - /*! - @brief an integer number was read - @param[in] val integer value - @return whether parsing should proceed - */ - virtual bool number_integer(number_integer_t val) = 0; - - /*! - @brief an unsigned integer number was read - @param[in] val unsigned integer value - @return whether parsing should proceed - */ - virtual bool number_unsigned(number_unsigned_t val) = 0; - - /*! - @brief an floating-point number was read - @param[in] val floating-point value - @param[in] s raw token value - @return whether parsing should proceed - */ - virtual bool number_float(number_float_t val, const string_t& s) = 0; - - /*! - @brief a string was read - @param[in] val string value - @return whether parsing should proceed - @note It is safe to move the passed string. - */ - virtual bool string(string_t& val) = 0; - - /*! - @brief a binary string was read - @param[in] val binary value - @return whether parsing should proceed - @note It is safe to move the passed binary. - */ - virtual bool binary(binary_t& val) = 0; - - /*! - @brief the beginning of an object was read - @param[in] elements number of object elements or -1 if unknown - @return whether parsing should proceed - @note binary formats may report the number of elements - */ - virtual bool start_object(std::size_t elements) = 0; - - /*! - @brief an object key was read - @param[in] val object key - @return whether parsing should proceed - @note It is safe to move the passed string. - */ - virtual bool key(string_t& val) = 0; - - /*! - @brief the end of an object was read - @return whether parsing should proceed - */ - virtual bool end_object() = 0; - - /*! - @brief the beginning of an array was read - @param[in] elements number of array elements or -1 if unknown - @return whether parsing should proceed - @note binary formats may report the number of elements - */ - virtual bool start_array(std::size_t elements) = 0; - - /*! - @brief the end of an array was read - @return whether parsing should proceed - */ - virtual bool end_array() = 0; - - /*! - @brief a parse error occurred - @param[in] position the position in the input where the error occurs - @param[in] last_token the last read token - @param[in] ex an exception object describing the error - @return whether parsing should proceed (must return false) - */ - virtual bool parse_error(std::size_t position, - const std::string& last_token, - const detail::exception& ex) = 0; - - json_sax() = default; - json_sax(const json_sax&) = default; - json_sax(json_sax&&) noexcept = default; - json_sax& operator=(const json_sax&) = default; - json_sax& operator=(json_sax&&) noexcept = default; - virtual ~json_sax() = default; -}; - - -namespace detail -{ -/*! -@brief SAX implementation to create a JSON value from SAX events - -This class implements the @ref json_sax interface and processes the SAX events -to create a JSON value which makes it basically a DOM parser. The structure or -hierarchy of the JSON value is managed by the stack `ref_stack` which contains -a pointer to the respective array or object for each recursion depth. - -After successful parsing, the value that is passed by reference to the -constructor contains the parsed value. - -@tparam BasicJsonType the JSON type -*/ -template -class json_sax_dom_parser -{ - public: - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; - - /*! - @param[in,out] r reference to a JSON value that is manipulated while - parsing - @param[in] allow_exceptions_ whether parse errors yield exceptions - */ - explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) - : root(r), allow_exceptions(allow_exceptions_) - {} - - // make class move-only - json_sax_dom_parser(const json_sax_dom_parser&) = delete; - json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; - json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - ~json_sax_dom_parser() = default; - - bool null() - { - handle_value(nullptr); - return true; - } - - bool boolean(bool val) - { - handle_value(val); - return true; - } - - bool number_integer(number_integer_t val) - { - handle_value(val); - return true; - } - - bool number_unsigned(number_unsigned_t val) - { - handle_value(val); - return true; - } - - bool number_float(number_float_t val, const string_t& /*unused*/) - { - handle_value(val); - return true; - } - - bool string(string_t& val) - { - handle_value(val); - return true; - } - - bool binary(binary_t& val) - { - handle_value(std::move(val)); - return true; - } - - bool start_object(std::size_t len) - { - ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); - - if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); - } - - return true; - } - - bool key(string_t& val) - { - // add null at given key and store the reference for later - object_element = &(ref_stack.back()->m_value.object->operator[](val)); - return true; - } - - bool end_object() - { - ref_stack.back()->set_parents(); - ref_stack.pop_back(); - return true; - } - - bool start_array(std::size_t len) - { - ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); - - if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); - } - - return true; - } - - bool end_array() - { - ref_stack.back()->set_parents(); - ref_stack.pop_back(); - return true; - } - - template - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const Exception& ex) - { - errored = true; - static_cast(ex); - if (allow_exceptions) - { - JSON_THROW(ex); - } - return false; - } - - constexpr bool is_errored() const - { - return errored; - } - - private: - /*! - @invariant If the ref stack is empty, then the passed value will be the new - root. - @invariant If the ref stack contains a value, then it is an array or an - object to which we can add elements - */ - template - JSON_HEDLEY_RETURNS_NON_NULL - BasicJsonType* handle_value(Value&& v) - { - if (ref_stack.empty()) - { - root = BasicJsonType(std::forward(v)); - return &root; - } - - JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); - - if (ref_stack.back()->is_array()) - { - ref_stack.back()->m_value.array->emplace_back(std::forward(v)); - return &(ref_stack.back()->m_value.array->back()); - } - - JSON_ASSERT(ref_stack.back()->is_object()); - JSON_ASSERT(object_element); - *object_element = BasicJsonType(std::forward(v)); - return object_element; - } - - /// the parsed JSON value - BasicJsonType& root; - /// stack to model hierarchy of values - std::vector ref_stack {}; - /// helper to hold the reference for the next object element - BasicJsonType* object_element = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; -}; - -template -class json_sax_dom_callback_parser -{ - public: - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; - using parser_callback_t = typename BasicJsonType::parser_callback_t; - using parse_event_t = typename BasicJsonType::parse_event_t; - - json_sax_dom_callback_parser(BasicJsonType& r, - const parser_callback_t cb, - const bool allow_exceptions_ = true) - : root(r), callback(cb), allow_exceptions(allow_exceptions_) - { - keep_stack.push_back(true); - } - - // make class move-only - json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; - json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; - json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - ~json_sax_dom_callback_parser() = default; - - bool null() - { - handle_value(nullptr); - return true; - } - - bool boolean(bool val) - { - handle_value(val); - return true; - } - - bool number_integer(number_integer_t val) - { - handle_value(val); - return true; - } - - bool number_unsigned(number_unsigned_t val) - { - handle_value(val); - return true; - } - - bool number_float(number_float_t val, const string_t& /*unused*/) - { - handle_value(val); - return true; - } - - bool string(string_t& val) - { - handle_value(val); - return true; - } - - bool binary(binary_t& val) - { - handle_value(std::move(val)); - return true; - } - - bool start_object(std::size_t len) - { - // check callback for object start - const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); - keep_stack.push_back(keep); - - auto val = handle_value(BasicJsonType::value_t::object, true); - ref_stack.push_back(val.second); - - // check object limit - if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); - } - - return true; - } - - bool key(string_t& val) - { - BasicJsonType k = BasicJsonType(val); - - // check callback for key - const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); - key_keep_stack.push_back(keep); - - // add discarded value at given key and store the reference for later - if (keep && ref_stack.back()) - { - object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); - } - - return true; - } - - bool end_object() - { - if (ref_stack.back()) - { - if (!callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) - { - // discard object - *ref_stack.back() = discarded; - } - else - { - ref_stack.back()->set_parents(); - } - } - - JSON_ASSERT(!ref_stack.empty()); - JSON_ASSERT(!keep_stack.empty()); - ref_stack.pop_back(); - keep_stack.pop_back(); - - if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) - { - // remove discarded value - for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) - { - if (it->is_discarded()) - { - ref_stack.back()->erase(it); - break; - } - } - } - - return true; - } - - bool start_array(std::size_t len) - { - const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); - keep_stack.push_back(keep); - - auto val = handle_value(BasicJsonType::value_t::array, true); - ref_stack.push_back(val.second); - - // check array limit - if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); - } - - return true; - } - - bool end_array() - { - bool keep = true; - - if (ref_stack.back()) - { - keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); - if (keep) - { - ref_stack.back()->set_parents(); - } - else - { - // discard array - *ref_stack.back() = discarded; - } - } - - JSON_ASSERT(!ref_stack.empty()); - JSON_ASSERT(!keep_stack.empty()); - ref_stack.pop_back(); - keep_stack.pop_back(); - - // remove discarded value - if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) - { - ref_stack.back()->m_value.array->pop_back(); - } - - return true; - } - - template - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const Exception& ex) - { - errored = true; - static_cast(ex); - if (allow_exceptions) - { - JSON_THROW(ex); - } - return false; - } - - constexpr bool is_errored() const - { - return errored; - } - - private: - /*! - @param[in] v value to add to the JSON value we build during parsing - @param[in] skip_callback whether we should skip calling the callback - function; this is required after start_array() and - start_object() SAX events, because otherwise we would call the - callback function with an empty array or object, respectively. - - @invariant If the ref stack is empty, then the passed value will be the new - root. - @invariant If the ref stack contains a value, then it is an array or an - object to which we can add elements - - @return pair of boolean (whether value should be kept) and pointer (to the - passed value in the ref_stack hierarchy; nullptr if not kept) - */ - template - std::pair handle_value(Value&& v, const bool skip_callback = false) - { - JSON_ASSERT(!keep_stack.empty()); - - // do not handle this value if we know it would be added to a discarded - // container - if (!keep_stack.back()) - { - return {false, nullptr}; - } - - // create value - auto value = BasicJsonType(std::forward(v)); - - // check callback - const bool keep = skip_callback || callback(static_cast(ref_stack.size()), parse_event_t::value, value); - - // do not handle this value if we just learnt it shall be discarded - if (!keep) - { - return {false, nullptr}; - } - - if (ref_stack.empty()) - { - root = std::move(value); - return {true, &root}; - } - - // skip this value if we already decided to skip the parent - // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) - if (!ref_stack.back()) - { - return {false, nullptr}; - } - - // we now only expect arrays and objects - JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); - - // array - if (ref_stack.back()->is_array()) - { - ref_stack.back()->m_value.array->emplace_back(std::move(value)); - return {true, &(ref_stack.back()->m_value.array->back())}; - } - - // object - JSON_ASSERT(ref_stack.back()->is_object()); - // check if we should store an element for the current key - JSON_ASSERT(!key_keep_stack.empty()); - const bool store_element = key_keep_stack.back(); - key_keep_stack.pop_back(); - - if (!store_element) - { - return {false, nullptr}; - } - - JSON_ASSERT(object_element); - *object_element = std::move(value); - return {true, object_element}; - } - - /// the parsed JSON value - BasicJsonType& root; - /// stack to model hierarchy of values - std::vector ref_stack {}; - /// stack to manage which values to keep - std::vector keep_stack {}; - /// stack to manage which object keys to keep - std::vector key_keep_stack {}; - /// helper to hold the reference for the next object element - BasicJsonType* object_element = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// callback function - const parser_callback_t callback = nullptr; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; - /// a discarded value for the callback - BasicJsonType discarded = BasicJsonType::value_t::discarded; -}; - -template -class json_sax_acceptor -{ - public: - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; - - bool null() - { - return true; - } - - bool boolean(bool /*unused*/) - { - return true; - } - - bool number_integer(number_integer_t /*unused*/) - { - return true; - } - - bool number_unsigned(number_unsigned_t /*unused*/) - { - return true; - } - - bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) - { - return true; - } - - bool string(string_t& /*unused*/) - { - return true; - } - - bool binary(binary_t& /*unused*/) - { - return true; - } - - bool start_object(std::size_t /*unused*/ = std::size_t(-1)) - { - return true; - } - - bool key(string_t& /*unused*/) - { - return true; - } - - bool end_object() - { - return true; - } - - bool start_array(std::size_t /*unused*/ = std::size_t(-1)) - { - return true; - } - - bool end_array() - { - return true; - } - - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) - { - return false; - } -}; -} // namespace detail - -} // namespace nlohmann +} +} // #include -#include // array #include // localeconv #include // size_t -#include // snprintf #include // strtof, strtod, strtold, strtoll, strtoull +#include // snprintf #include // initializer_list #include // char_traits, string -#include // move #include // vector -// #include - -// #include - // #include +// #include + namespace nlohmann { @@ -6556,9 +2234,19 @@ namespace detail // lexer // /////////// +/*! +@brief lexical analysis + +This class organizes the lexical analysis during JSON deserialization. +*/ template -class lexer_base +class lexer { + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + public: /// token types for the parser enum class token_type @@ -6583,8 +2271,6 @@ class lexer_base }; /// return name of values of type token_type (only used for errors) - JSON_HEDLEY_RETURNS_NON_NULL - JSON_HEDLEY_CONST static const char* token_type_name(const token_type t) noexcept { switch (t) @@ -6599,9 +2285,9 @@ class lexer_base return "null literal"; case token_type::value_string: return "string literal"; - case token_type::value_unsigned: - case token_type::value_integer: - case token_type::value_float: + case lexer::token_type::value_unsigned: + case lexer::token_type::value_integer: + case lexer::token_type::value_float: return "number literal"; case token_type::begin_array: return "'['"; @@ -6627,37 +2313,13 @@ class lexer_base // LCOV_EXCL_STOP } } -}; -/*! -@brief lexical analysis -This class organizes the lexical analysis during JSON deserialization. -*/ -template -class lexer : public lexer_base -{ - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using char_type = typename InputAdapterType::char_type; - using char_int_type = typename std::char_traits::int_type; - - public: - using token_type = typename lexer_base::token_type; - - explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept - : ia(std::move(adapter)) - , ignore_comments(ignore_comments_) - , decimal_point_char(static_cast(get_decimal_point())) - {} + explicit lexer(detail::input_adapter_t&& adapter) + : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {} // delete because of pointer members lexer(const lexer&) = delete; - lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) lexer& operator=(lexer&) = delete; - lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - ~lexer() = default; private: ///////////////////// @@ -6665,11 +2327,10 @@ class lexer : public lexer_base ///////////////////// /// return the locale-dependent decimal point - JSON_HEDLEY_PURE static char get_decimal_point() noexcept { - const auto* loc = localeconv(); - JSON_ASSERT(loc != nullptr); + const auto loc = localeconv(); + assert(loc != nullptr); return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } @@ -6695,25 +2356,25 @@ class lexer : public lexer_base int get_codepoint() { // this function only makes sense after reading `\u` - JSON_ASSERT(current == 'u'); + assert(current == 'u'); int codepoint = 0; - const auto factors = { 12u, 8u, 4u, 0u }; + const auto factors = { 12, 8, 4, 0 }; for (const auto factor : factors) { get(); - if (current >= '0' && current <= '9') + if (current >= '0' and current <= '9') { - codepoint += static_cast((static_cast(current) - 0x30u) << factor); + codepoint += ((current - 0x30) << factor); } - else if (current >= 'A' && current <= 'F') + else if (current >= 'A' and current <= 'F') { - codepoint += static_cast((static_cast(current) - 0x37u) << factor); + codepoint += ((current - 0x37) << factor); } - else if (current >= 'a' && current <= 'f') + else if (current >= 'a' and current <= 'f') { - codepoint += static_cast((static_cast(current) - 0x57u) << factor); + codepoint += ((current - 0x57) << factor); } else { @@ -6721,7 +2382,7 @@ class lexer : public lexer_base } } - JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF); + assert(0x0000 <= codepoint and codepoint <= 0xFFFF); return codepoint; } @@ -6740,15 +2401,15 @@ class lexer : public lexer_base @return true if and only if no range violation was detected */ - bool next_byte_in_range(std::initializer_list ranges) + bool next_byte_in_range(std::initializer_list ranges) { - JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6); + assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); add(current); for (auto range = ranges.begin(); range != ranges.end(); ++range) { get(); - if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) + if (JSON_LIKELY(*range <= current and current <= *(++range))) { add(current); } @@ -6765,7 +2426,7 @@ class lexer : public lexer_base /*! @brief scan a string literal - This function scans a string according to Sect. 7 of RFC 8259. While + This function scans a string according to Sect. 7 of RFC 7159. While scanning, bytes are escaped and copied into buffer token_buffer. Then the function returns successfully, token_buffer is *not* null-terminated (as it may contain \0 bytes), and token_buffer.size() is the number of bytes in the @@ -6783,7 +2444,7 @@ class lexer : public lexer_base reset(); // we entered the function by reading an open quote - JSON_ASSERT(current == '\"'); + assert(current == '\"'); while (true) { @@ -6791,7 +2452,7 @@ class lexer : public lexer_base switch (get()) { // end of file while parsing string - case std::char_traits::eof(): + case std::char_traits::eof(): { error_message = "invalid string: missing closing quote"; return token_type::parse_error; @@ -6847,55 +2508,55 @@ class lexer : public lexer_base const int codepoint1 = get_codepoint(); int codepoint = codepoint1; // start with codepoint1 - if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) + if (JSON_UNLIKELY(codepoint1 == -1)) { error_message = "invalid string: '\\u' must be followed by 4 hex digits"; return token_type::parse_error; } // check if code point is a high surrogate - if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) + if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF) { // expect next \uxxxx entry - if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u')) + if (JSON_LIKELY(get() == '\\' and get() == 'u')) { const int codepoint2 = get_codepoint(); - if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) + if (JSON_UNLIKELY(codepoint2 == -1)) { error_message = "invalid string: '\\u' must be followed by 4 hex digits"; return token_type::parse_error; } // check if codepoint2 is a low surrogate - if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF)) + if (JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) { // overwrite codepoint - codepoint = static_cast( - // high surrogate occupies the most significant 22 bits - (static_cast(codepoint1) << 10u) - // low surrogate occupies the least significant 15 bits - + static_cast(codepoint2) - // there is still the 0xD800, 0xDC00 and 0x10000 noise - // in the result so we have to subtract with: - // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - - 0x35FDC00u); + codepoint = + // high surrogate occupies the most significant 22 bits + (codepoint1 << 10) + // low surrogate occupies the least significant 15 bits + + codepoint2 + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00; } else { - error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; return token_type::parse_error; } } else { - error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; return token_type::parse_error; } } else { - if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) + if (JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) { error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; return token_type::parse_error; @@ -6903,34 +2564,34 @@ class lexer : public lexer_base } // result of the above calculation yields a proper codepoint - JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); + assert(0x00 <= codepoint and codepoint <= 0x10FFFF); // translate codepoint into bytes if (codepoint < 0x80) { // 1-byte characters: 0xxxxxxx (ASCII) - add(static_cast(codepoint)); + add(codepoint); } else if (codepoint <= 0x7FF) { // 2-byte characters: 110xxxxx 10xxxxxx - add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + add(0xC0 | (codepoint >> 6)); + add(0x80 | (codepoint & 0x3F)); } else if (codepoint <= 0xFFFF) { // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); - add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + add(0xE0 | (codepoint >> 12)); + add(0x80 | ((codepoint >> 6) & 0x3F)); + add(0x80 | (codepoint & 0x3F)); } else { // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); - add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); - add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + add(0xF0 | (codepoint >> 18)); + add(0x80 | ((codepoint >> 12) & 0x3F)); + add(0x80 | ((codepoint >> 6) & 0x3F)); + add(0x80 | (codepoint & 0x3F)); } break; @@ -6947,194 +2608,39 @@ class lexer : public lexer_base // invalid control characters case 0x00: - { - error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; - return token_type::parse_error; - } - case 0x01: - { - error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; - return token_type::parse_error; - } - case 0x02: - { - error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; - return token_type::parse_error; - } - case 0x03: - { - error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; - return token_type::parse_error; - } - case 0x04: - { - error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; - return token_type::parse_error; - } - case 0x05: - { - error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; - return token_type::parse_error; - } - case 0x06: - { - error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; - return token_type::parse_error; - } - case 0x07: - { - error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; - return token_type::parse_error; - } - case 0x08: - { - error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; - return token_type::parse_error; - } - case 0x09: - { - error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; - return token_type::parse_error; - } - case 0x0A: - { - error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; - return token_type::parse_error; - } - case 0x0B: - { - error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; - return token_type::parse_error; - } - case 0x0C: - { - error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; - return token_type::parse_error; - } - case 0x0D: - { - error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; - return token_type::parse_error; - } - case 0x0E: - { - error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; - return token_type::parse_error; - } - case 0x0F: - { - error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; - return token_type::parse_error; - } - case 0x10: - { - error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; - return token_type::parse_error; - } - case 0x11: - { - error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; - return token_type::parse_error; - } - case 0x12: - { - error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; - return token_type::parse_error; - } - case 0x13: - { - error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; - return token_type::parse_error; - } - case 0x14: - { - error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; - return token_type::parse_error; - } - case 0x15: - { - error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; - return token_type::parse_error; - } - case 0x16: - { - error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; - return token_type::parse_error; - } - case 0x17: - { - error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; - return token_type::parse_error; - } - case 0x18: - { - error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; - return token_type::parse_error; - } - case 0x19: - { - error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; - return token_type::parse_error; - } - case 0x1A: - { - error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; - return token_type::parse_error; - } - case 0x1B: - { - error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; - return token_type::parse_error; - } - case 0x1C: - { - error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; - return token_type::parse_error; - } - case 0x1D: - { - error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; - return token_type::parse_error; - } - case 0x1E: - { - error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; - return token_type::parse_error; - } - case 0x1F: { - error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + error_message = "invalid string: control character must be escaped"; return token_type::parse_error; } @@ -7270,7 +2776,7 @@ class lexer : public lexer_base case 0xDE: case 0xDF: { - if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF}))) + if (JSON_UNLIKELY(not next_byte_in_range({0x80, 0xBF}))) { return token_type::parse_error; } @@ -7280,7 +2786,7 @@ class lexer : public lexer_base // U+0800..U+0FFF: bytes E0 A0..BF 80..BF case 0xE0: { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + if (JSON_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -7304,7 +2810,7 @@ class lexer : public lexer_base case 0xEE: case 0xEF: { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -7314,7 +2820,7 @@ class lexer : public lexer_base // U+D000..U+D7FF: bytes ED 80..9F 80..BF case 0xED: { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -7324,7 +2830,7 @@ class lexer : public lexer_base // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF case 0xF0: { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + if (JSON_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -7336,7 +2842,7 @@ class lexer : public lexer_base case 0xF2: case 0xF3: { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -7346,7 +2852,7 @@ class lexer : public lexer_base // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF case 0xF4: { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -7363,90 +2869,16 @@ class lexer : public lexer_base } } - /*! - * @brief scan a comment - * @return whether comment could be scanned successfully - */ - bool scan_comment() - { - switch (get()) - { - // single-line comments skip input until a newline or EOF is read - case '/': - { - while (true) - { - switch (get()) - { - case '\n': - case '\r': - case std::char_traits::eof(): - case '\0': - return true; - - default: - break; - } - } - } - - // multi-line comments skip input until */ is read - case '*': - { - while (true) - { - switch (get()) - { - case std::char_traits::eof(): - case '\0': - { - error_message = "invalid comment; missing closing '*/'"; - return false; - } - - case '*': - { - switch (get()) - { - case '/': - return true; - - default: - { - unget(); - continue; - } - } - } - - default: - continue; - } - } - } - - // unexpected character after reading '/' - default: - { - error_message = "invalid comment; expecting '/' or '*' after '/'"; - return false; - } - } - } - - JSON_HEDLEY_NON_NULL(2) static void strtof(float& f, const char* str, char** endptr) noexcept { f = std::strtof(str, endptr); } - JSON_HEDLEY_NON_NULL(2) static void strtof(double& f, const char* str, char** endptr) noexcept { f = std::strtod(str, endptr); } - JSON_HEDLEY_NON_NULL(2) static void strtof(long double& f, const char* str, char** endptr) noexcept { f = std::strtold(str, endptr); @@ -7455,10 +2887,10 @@ class lexer : public lexer_base /*! @brief scan a number literal - This function scans a string according to Sect. 6 of RFC 8259. + This function scans a string according to Sect. 6 of RFC 7159. The function is realized with a deterministic finite state machine derived - from the grammar described in RFC 8259. Starting in state "init", the + from the grammar described in RFC 7159. Starting in state "init", the input is read and used to determined the next state. Only state "done" accepts the number. State "error" is a trap state to model errors. In the table below, "anything" means any character but the ones listed before. @@ -7469,7 +2901,7 @@ class lexer : public lexer_base minus | zero | any1 | [error] | [error] | [error] | [error] | [error] zero | done | done | exponent | done | done | decimal1 | done any1 | any1 | any1 | exponent | done | done | decimal1 | done - decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] + decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error] decimal2 | decimal2 | decimal2 | exponent | done | done | done | done exponent | any2 | any2 | [error] | sign | sign | [error] | [error] sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] @@ -7492,7 +2924,7 @@ class lexer : public lexer_base locale's decimal point is used instead of `.` to work with the locale-dependent converters. */ - token_type scan_number() // lgtm [cpp/use-of-goto] + token_type scan_number() { // reset token_buffer to store the number's bytes reset(); @@ -7530,9 +2962,13 @@ class lexer : public lexer_base goto scan_number_any1; } - // all other characters are rejected outside scan_number() - default: // LCOV_EXCL_LINE - JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + // LCOV_EXCL_START + default: + { + // all other characters are rejected outside scan_number() + assert(false); + } + // LCOV_EXCL_STOP } scan_number_minus: @@ -7770,7 +3206,7 @@ scan_number_done: // we are done scanning a number) unget(); - char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + char* endptr = nullptr; errno = 0; // try to parse integers first and fall back to floats @@ -7779,7 +3215,7 @@ scan_number_done: const auto x = std::strtoull(token_buffer.data(), &endptr, 10); // we checked the number format before - JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + assert(endptr == token_buffer.data() + token_buffer.size()); if (errno == 0) { @@ -7795,7 +3231,7 @@ scan_number_done: const auto x = std::strtoll(token_buffer.data(), &endptr, 10); // we checked the number format before - JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + assert(endptr == token_buffer.data() + token_buffer.size()); if (errno == 0) { @@ -7812,7 +3248,7 @@ scan_number_done: strtof(value_float, token_buffer.data(), &endptr); // we checked the number format before - JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + assert(endptr == token_buffer.data() + token_buffer.size()); return token_type::value_float; } @@ -7822,14 +3258,13 @@ scan_number_done: @param[in] length the length of the passed literal text @param[in] return_type the token type to return on success */ - JSON_HEDLEY_NON_NULL(2) - token_type scan_literal(const char_type* literal_text, const std::size_t length, + token_type scan_literal(const char* literal_text, const std::size_t length, token_type return_type) { - JSON_ASSERT(std::char_traits::to_char_type(current) == literal_text[0]); + assert(current == literal_text[0]); for (std::size_t i = 1; i < length; ++i) { - if (JSON_HEDLEY_UNLIKELY(std::char_traits::to_char_type(get()) != literal_text[i])) + if (JSON_UNLIKELY(get() != literal_text[i])) { error_message = "invalid literal"; return token_type::parse_error; @@ -7847,7 +3282,7 @@ scan_number_done: { token_buffer.clear(); token_string.clear(); - token_string.push_back(std::char_traits::to_char_type(current)); + token_string.push_back(std::char_traits::to_char_type(current)); } /* @@ -7860,11 +3295,9 @@ scan_number_done: @return character read from the input */ - char_int_type get() + std::char_traits::int_type get() { - ++position.chars_read_total; - ++position.chars_read_current_line; - + ++chars_read; if (next_unget) { // just reset the next_unget variable and work with current @@ -7872,20 +3305,13 @@ scan_number_done: } else { - current = ia.get_character(); + current = ia->get_character(); } - if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + if (JSON_LIKELY(current != std::char_traits::eof())) { - token_string.push_back(std::char_traits::to_char_type(current)); + token_string.push_back(std::char_traits::to_char_type(current)); } - - if (current == '\n') - { - ++position.lines_read; - position.chars_read_current_line = 0; - } - return current; } @@ -7893,40 +3319,25 @@ scan_number_done: @brief unget current character (read it again on next get) We implement unget by setting variable next_unget to true. The input is not - changed - we just simulate ungetting by modifying chars_read_total, - chars_read_current_line, and token_string. The next call to get() will - behave as if the unget character is read again. + changed - we just simulate ungetting by modifying chars_read and + token_string. The next call to get() will behave as if the unget character + is read again. */ void unget() { next_unget = true; - - --position.chars_read_total; - - // in case we "unget" a newline, we have to also decrement the lines_read - if (position.chars_read_current_line == 0) + --chars_read; + if (JSON_LIKELY(current != std::char_traits::eof())) { - if (position.lines_read > 0) - { - --position.lines_read; - } - } - else - { - --position.chars_read_current_line; - } - - if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) - { - JSON_ASSERT(!token_string.empty()); + assert(token_string.size() != 0); token_string.pop_back(); } } /// add a character to token_buffer - void add(char_int_type c) + void add(int c) { - token_buffer.push_back(static_cast(c)); + token_buffer.push_back(std::char_traits::to_char_type(c)); } public: @@ -7963,9 +3374,9 @@ scan_number_done: ///////////////////// /// return position of last read token - constexpr position_t get_position() const noexcept + constexpr std::size_t get_position() const noexcept { - return position; + return chars_read; } /// return the last read token (for errors only). Will never contain EOF @@ -7977,17 +3388,17 @@ scan_number_done: std::string result; for (const auto c : token_string) { - if (static_cast(c) <= '\x1F') + if ('\x00' <= c and c <= '\x1F') { // escape control characters - std::array cs{{}}; - (std::snprintf)(cs.data(), cs.size(), "", static_cast(c)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) - result += cs.data(); + char cs[9]; + snprintf(cs, 9, "", static_cast(c)); + result += cs; } else { // add character as is - result.push_back(static_cast(c)); + result.push_back(c); } } @@ -7995,7 +3406,6 @@ scan_number_done: } /// return syntax error message - JSON_HEDLEY_RETURNS_NON_NULL constexpr const char* get_error_message() const noexcept { return error_message; @@ -8013,48 +3423,41 @@ scan_number_done: { if (get() == 0xEF) { - // check if we completely parse the BOM - return get() == 0xBB && get() == 0xBF; + if (get() == 0xBB and get() == 0xBF) + { + // we completely parsed the BOM + return true; + } + else + { + // after reading 0xEF, an unexpected character followed + return false; + } } - - // the first character is not the beginning of the BOM; unget it to - // process is later - unget(); - return true; - } - - void skip_whitespace() - { - do + else { - get(); + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); + return true; } - while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); } token_type scan() { // initially, skip the BOM - if (position.chars_read_total == 0 && !skip_bom()) + if (chars_read == 0 and not skip_bom()) { error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; return token_type::parse_error; } // read next character and ignore whitespace - skip_whitespace(); - - // ignore comments - while (ignore_comments && current == '/') + do { - if (!scan_comment()) - { - return token_type::parse_error; - } - - // skip following whitespace - skip_whitespace(); + get(); } + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); switch (current) { @@ -8074,20 +3477,11 @@ scan_number_done: // literals case 't': - { - std::array true_literal = {{char_type('t'), char_type('r'), char_type('u'), char_type('e')}}; - return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); - } + return scan_literal("true", 4, token_type::literal_true); case 'f': - { - std::array false_literal = {{char_type('f'), char_type('a'), char_type('l'), char_type('s'), char_type('e')}}; - return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); - } + return scan_literal("false", 5, token_type::literal_false); case 'n': - { - std::array null_literal = {{char_type('n'), char_type('u'), char_type('l'), char_type('l')}}; - return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); - } + return scan_literal("null", 4, token_type::literal_null); // string case '\"': @@ -8110,7 +3504,7 @@ scan_number_done: // end of input (the null byte is needed when parsing from // string literals) case '\0': - case std::char_traits::eof(): + case std::char_traits::eof(): return token_type::end_of_input; // error @@ -8122,22 +3516,19 @@ scan_number_done: private: /// input adapter - InputAdapterType ia; - - /// whether comments should be ignored (true) or signaled as errors (false) - const bool ignore_comments = false; + detail::input_adapter_t ia = nullptr; /// the current character - char_int_type current = std::char_traits::eof(); + std::char_traits::int_type current = std::char_traits::eof(); /// whether the next get() call should just return current bool next_unget = false; - /// the start position of the current token - position_t position {}; + /// the number of characters read + std::size_t chars_read = 0; /// raw input token string (for error messages) - std::vector token_string {}; + std::vector token_string {}; /// buffer for variable-length tokens (numbers, strings) string_t token_buffer {}; @@ -8151,10 +3542,22 @@ scan_number_done: number_float_t value_float = 0; /// the decimal point - const char_int_type decimal_point_char = '.'; + const char decimal_point_char = '.'; }; -} // namespace detail -} // namespace nlohmann +} +} + +// #include + + +#include // assert +#include // isfinite +#include // uint8_t +#include // function +#include // string +#include // move + +// #include // #include @@ -8163,10 +3566,76 @@ scan_number_done: #include // size_t #include // declval -#include // string // #include + +#include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +using void_t = void; +} +} + + +// http://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + void operator=(nonesuch const&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template