├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .gitmodules ├── .npmignore ├── LICENSE ├── Makefile ├── README.md ├── index.d.ts ├── package.json ├── patches ├── 0001-fix-permission-issues-with-nodefs.patch ├── 0002-chore-disable-error-reporting.patch ├── 0003-chore-remove-file-and-line-info.patch └── 0004-chore-ssh-allow-for-fake-ssh-support.patch ├── pnpm-lock.yaml └── src ├── index.js ├── interface ├── alloc.js ├── exports.js └── http.js └── lib ├── http.c ├── main.c └── ssh.c /.eslintignore: -------------------------------------------------------------------------------- 1 | src/wasm/* 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | // todo: make better lol 3 | "env": { 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": [ "eslint:recommended" ], 8 | "rules": { 9 | "no-process-exit": "off", 10 | "no-undef": "off", 11 | "no-unused-vars": "error", 12 | "no-redeclare": "error", 13 | 14 | // Possible errors 15 | "no-loss-of-precision": "error", 16 | "no-extra-parens": [ 17 | "error", "all", 18 | { 19 | "conditionalAssign": false, 20 | "returnAssign": false, 21 | "nestedBinaryExpressions": false, 22 | "ignoreJSX": "multi-line" 23 | } 24 | ], 25 | "no-template-curly-in-string": "error", 26 | "no-sparse-arrays": "error", 27 | "no-unreachable-loop": "error", 28 | "no-useless-backreference": "error", 29 | "require-atomic-updates": "error", 30 | 31 | // Best practices 32 | "class-methods-use-this": "error", 33 | "default-case-last": "error", 34 | "default-param-last": "error", 35 | "dot-location": [ "error", "property" ], 36 | "dot-notation": "error", 37 | "eqeqeq": "error", 38 | "grouped-accessor-pairs": [ "error", "getBeforeSet" ], 39 | "guard-for-in": "error", 40 | "no-alert": "error", 41 | "no-caller": "error", 42 | "no-constructor-return": "error", 43 | "no-else-return": "error", 44 | "no-empty-function": "error", 45 | "no-eval": "error", 46 | "no-extend-native": "error", 47 | "no-extra-bind": "error", 48 | "no-floating-decimal": "error", 49 | "no-implicit-coercion": "error", 50 | "no-implied-eval": "error", 51 | "no-invalid-this": "error", 52 | "no-iterator": "error", 53 | "no-labels": "error", 54 | "no-lone-blocks": "error", 55 | "no-loop-func": "error", 56 | "no-multi-spaces": "error", 57 | "no-new": "error", 58 | "no-new-wrappers": "error", 59 | "no-octal": "error", 60 | "no-octal-escape": "error", 61 | "no-proto": "error", 62 | "no-return-assign": [ "error", "except-parens" ], 63 | "no-return-await": "error", 64 | "no-self-compare": "error", 65 | "no-sequences": "error", 66 | "no-throw-literal": "error", 67 | "no-unmodified-loop-condition": "error", 68 | "no-unused-expressions": "error", 69 | "no-warning-comments": "warn", 70 | "prefer-promise-reject-errors": "error", 71 | "radix": "error", 72 | "wrap-iife": "error", 73 | "yoda": "error", 74 | 75 | // Vars 76 | "no-shadow": "error", 77 | "no-undefined": "error", 78 | "no-use-before-define": "error", 79 | 80 | // Stylistic 81 | "array-bracket-newline": [ "error", "consistent" ], 82 | "array-bracket-spacing": [ "error", "always" ], 83 | "block-spacing": "error", 84 | "brace-style": [ "error", "1tbs", { "allowSingleLine": true } ], 85 | // "camelcase": "error", 86 | "comma-dangle": [ 87 | "error", 88 | { 89 | "arrays": "always-multiline", 90 | "objects": "always-multiline", 91 | "imports": "always-multiline", 92 | "exports": "always-multiline", 93 | "functions": "never" 94 | } 95 | ], 96 | "comma-spacing": "error", 97 | "comma-style": "error", 98 | "computed-property-spacing": "error", 99 | "eol-last": "error", 100 | "func-call-spacing": "error", 101 | "func-name-matching": "error", 102 | "function-call-argument-newline": [ "error", "consistent" ], 103 | "function-paren-newline": [ "error", "multiline-arguments" ], 104 | "indent": [ "error", 2, { "SwitchCase": 1 } ], 105 | "jsx-quotes": [ "error", "prefer-single" ], 106 | "key-spacing": "error", 107 | "keyword-spacing": "error", 108 | "linebreak-style": [ "error", "unix" ], 109 | "lines-between-class-members": "error", 110 | "multiline-ternary": [ "error", "always-multiline" ], 111 | "new-cap": "error", 112 | "new-parens": "error", 113 | "no-array-constructor": "error", 114 | "no-lonely-if": "error", 115 | "no-mixed-operators": "error", 116 | "no-multi-assign": "error", 117 | "no-multiple-empty-lines": "error", 118 | "no-new-object": "error", 119 | "no-tabs": "error", 120 | "no-trailing-spaces": "error", 121 | "no-unneeded-ternary": "error", 122 | "no-whitespace-before-property": "error", 123 | "nonblock-statement-body-position": "error", 124 | "object-curly-newline": [ "error", { "multiline": true } ], 125 | "object-curly-spacing": [ "error", "always" ], 126 | "object-property-newline": [ "error", { "allowAllPropertiesOnSameLine": true } ], 127 | "one-var": [ "error", "never" ], 128 | "operator-assignment": "error", 129 | "operator-linebreak": [ "error", "before" ], 130 | "padded-blocks": [ "error", "never" ], 131 | "prefer-exponentiation-operator": "error", 132 | "quotes": [ "error", "single" ], 133 | "semi": [ "error", "never" ], 134 | "semi-style": [ "error", "first" ], 135 | "space-before-blocks": "error", 136 | "space-before-function-paren": "error", 137 | "space-in-parens": "error", 138 | "space-infix-ops": "error", 139 | "space-unary-ops": [ "error", { "words": true, "nonwords": false } ], 140 | "switch-colon-spacing": "error", 141 | "template-tag-spacing": "error", 142 | "unicode-bom": "error", 143 | 144 | // ES6 145 | "arrow-body-style": "error", 146 | "arrow-parens": "error", 147 | "arrow-spacing": "error", 148 | "generator-star-spacing": [ 149 | "error", 150 | { 151 | "named": "after", 152 | "anonymous": "after", 153 | "method": "before" 154 | } 155 | ], 156 | "no-useless-computed-key": "error", 157 | "no-useless-constructor": "error", 158 | "no-useless-rename": "error", 159 | "no-var": "error", 160 | "object-shorthand": [ "error", "never" ], 161 | "prefer-arrow-callback": "error", 162 | "prefer-rest-params": "error", 163 | "prefer-template": "error", 164 | "rest-spread-spacing": "error", 165 | "symbol-description": "error", 166 | "template-curly-spacing": "error", 167 | "yield-star-spacing": [ "error", "after" ] 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | src/wasm 3 | *.nogit/ 4 | *.nogit.* 5 | node_modules 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libgit2"] 2 | path = libgit2 3 | url = https://github.com/libgit2/libgit2 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | patches 3 | src/lib 4 | src/interface 5 | libgit2 6 | .gitmodules 7 | Makefile 8 | *.nogit/ 9 | *.nogit.* 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Cynthia K. Rey, All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright notice, 9 | this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | 3. Neither the name of the copyright holder nor the names of its contributors 12 | may be used to endorse or promote products derived from this software without 13 | specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021 Cynthia K. Rey, All rights reserved. 2 | # 3 | # Redistribution and use in source and binary forms, with or without 4 | # modification, are permitted provided that the following conditions are met: 5 | # 6 | # 1. Redistributions of source code must retain the above copyright notice, this 7 | # list of conditions and the following disclaimer. 8 | # 2. Redistributions in binary form must reproduce the above copyright notice, 9 | # this list of conditions and the following disclaimer in the 10 | # documentation and/or other materials provided with the distribution. 11 | # 3. Neither the name of the copyright holder nor the names of its contributors 12 | # may be used to endorse or promote products derived from this software without 13 | # specific prior written permission. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | EMCC_FLAGS= -Wno-pthreads-mem-growth \ 27 | --pre-js src/interface/alloc.js \ 28 | --pre-js src/interface/http.js \ 29 | --post-js src/interface/exports.js \ 30 | -I libgit2/include \ 31 | -s MODULARIZE \ 32 | -s EXPORTED_RUNTIME_METHODS=writeArrayToMemory,lengthBytesUTF8,stringToUTF8 \ 33 | -s ENVIRONMENT=node \ 34 | -s ALLOW_MEMORY_GROWTH \ 35 | -s EXPORT_NAME=WasmModule \ 36 | -s PTHREAD_POOL_SIZE='Math.max(require("os").cpus().length - 1, 1)' \ 37 | -s PTHREAD_POOL_SIZE_STRICT=2 \ 38 | -s TEXTDECODER=2 \ 39 | -s MALLOC=emmalloc \ 40 | -s SUPPORT_LONGJMP=0 \ 41 | -s NODEJS_CATCH_REJECTION=0 \ 42 | -s NODEJS_CATCH_EXIT=0 \ 43 | -s INCOMING_MODULE_JS_API=[] \ 44 | -s AUTO_JS_LIBRARIES=0 \ 45 | -s AUTO_NATIVE_LIBRARIES=0 \ 46 | -s AUTO_ARCHIVE_INDEXES=0 \ 47 | -pthread \ 48 | -lnodefs.js \ 49 | -o src/wasm/libgit.js \ 50 | libgit2/build/libgit2.a \ 51 | src/lib/main.c 52 | 53 | EMCC_DEBUG_FLAGS= -O0 -g3 \ 54 | -s LLD_REPORT_UNDEFINED \ 55 | -s ASSERTIONS=1 56 | 57 | EMCC_BUILD_FLAGS= -Oz --closure 1 -s USE_CLOSURE_COMPILER 58 | 59 | libgit2/build/libgit2.a: 60 | mkdir libgit2/build || true 61 | cd libgit2 && git reset --hard 62 | 63 | # Patches to reduce weight of the final binary & some fixes 64 | cd libgit2 && git apply ../patches/*.patch 65 | 66 | # Use our own HTTP transport 67 | rm libgit2/src/transports/http.c || true 68 | rm libgit2/src/transports/ssh.c || true 69 | ln -s ../../../src/lib/http.c libgit2/src/transports/http.c 70 | ln -s ../../../src/lib/ssh.c libgit2/src/transports/ssh.c 71 | 72 | # Build the lib 73 | cd libgit2/build && emcmake cmake \ 74 | -DCMAKE_BUILD_TYPE=Release \ 75 | -DCMAKE_C_FLAGS="-Oz -pthread" \ 76 | -DBUILD_SHARED_LIBS=OFF \ 77 | -DBUILD_CLAR=OFF \ 78 | -DUSE_HTTPS=OFF \ 79 | -DUSE_SSH=OFF \ 80 | -DREGEX_BACKEND=regcomp \ 81 | .. 82 | cd libgit2/build && emmake make -j4 83 | 84 | .PHONY: build 85 | build: libgit2/build/libgit2.a 86 | rm -rf src/wasm || true 87 | mkdir src/wasm 88 | emcc $(EMCC_FLAGS) $(EMCC_BUILD_FLAGS) 89 | # Ensure we get no surprises if this lib is used in Electron 90 | sed -i "s/require(\"fs\")/require(process.versions.electron ? \"original-fs\" : \"fs\")/g" src/wasm/libgit.js src/wasm/libgit.worker.js 91 | 92 | .PHONY: debug 93 | debug: libgit2/build/libgit2.a 94 | rm -rf src/wasm || true 95 | mkdir src/wasm 96 | emcc $(EMCC_FLAGS) $(EMCC_DEBUG_FLAGS) 97 | 98 | .PHONY: clear 99 | clear: 100 | rm -rf libgit2/build || true 101 | emcc --clear-cache 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `simple-git-wasm` 2 | A (relatively) small node library to clone and pull git repositories in a standalone manner thanks to 3 | [libgit2](https://github.com/libgit2/libgit2), powered by [WebAssembly](https://webassembly.org/) and 4 | [Emscripten](https://emscripten.org). 5 | 6 | Made to be used as part of the [Powercord](https://powercord.dev) built-in updater. Despite the set purpose, the lib 7 | can be used by anyone who wishes to if it fits their use case. 8 | 9 | ## Why? 10 | Not happy with the solutions I had. [isomorphic-git](https://github.com/isomorphic-git/isomorphic-git) is a HEAVY 11 | beast, and [wasm-git](https://github.com/petersalomonsen/wasm-git) is too raw for me, me like some syntax sugar. 12 | Makes my software sweeter. It also doesn't support things it cannot support because of its target (I only care 13 | about Node, so I can make different design choices). 14 | 15 | ## Usage 16 | Installation: 17 | ``` 18 | [pnpm | yarn | npm] i @powercord/simple-git-wasm 19 | ``` 20 | 21 | ### Clone a repository 22 | **Note**: Submodules will be cloned as well. 23 | ```js 24 | const sgw = require('@powercord/simple-git-wasm') 25 | 26 | try { 27 | await sgw.clone('https://github.com/powercord-org/simple-git-wasm', './sgw') 28 | } catch (e) { 29 | console.error('An error occurred while cloning the repository!') 30 | } 31 | ``` 32 | ### Pull a repository 33 | **Note**: Submodules will be updated if necessary. 34 | ```js 35 | const sgw = require('@powercord/simple-git-wasm') 36 | 37 | try { 38 | await sgw.pull('./sgw') 39 | } catch (e) { 40 | console.error('An error occurred while pulling the repository!') 41 | } 42 | ``` 43 | 44 | ### Check for updates (new commits) 45 | ```js 46 | const sgw = require('@powercord/simple-git-wasm') 47 | 48 | try { 49 | const updates = await sgw.listUpdates('./sgw') 50 | console.log(updates) 51 | // ~> [ 52 | // ~> { id: 'abcdef.....', message: 'This is the newest commit', author: 'Cynthia' }, 53 | // ~> { id: 'abcdef.....', message: 'This is a new commit', author: 'Cynthia' }, 54 | // ~> { id: 'abcdef.....', message: 'This is the oldest new commit', author: 'Cynthia' }, 55 | // ~> ] 56 | } catch (e) { 57 | console.error('An error occurred while pulling the repository!') 58 | } 59 | ``` 60 | 61 | ## Notes 62 | The following PRs are required for this to work: 63 | - https://github.com/emscripten-core/emscripten/pull/14722 64 | 65 | I (Cynthia) patched my Emscripten installation to strip things not needed but included in the final build. 66 | - https://github.com/emscripten-core/emscripten/issues/11805 (patch in the comments) 67 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Cynthia K. Rey, All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. Neither the name of the copyright holder nor the names of its contributors 13 | * may be used to endorse or promote products derived from this software without 14 | * specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | declare module '@powercord/simple-git-wasm' { 29 | export type Commit = { 30 | id: string, 31 | message: string, 32 | author: string, 33 | date: Date 34 | } 35 | 36 | export type RepositoryMeta = { 37 | detached: boolean, 38 | branch: string | null, 39 | revision: string | null, 40 | upstream: string | null, 41 | repo: string | null 42 | } 43 | 44 | /** 45 | * Clones a repository in a specified path. Only HTTP(S) urls are supported. 46 | * @param repository the repository to clone. 47 | * @param path destination path. must be an empty (or not yet existing) directory. 48 | */ 49 | export async function clone (repository: string, path: string): Promise 50 | 51 | /** 52 | * Pulls new commits to a repository. 53 | * @param path path where the repository resides. 54 | * @param skipFetch set to true to pull the already fetched refs, not checking if there are new ones. 55 | * @param force if a conflict happens, stash changes and pull on a clean working tree 56 | */ 57 | export async function pull (path: string, skipFetch?: boolean, force?: boolean): Promise 58 | 59 | /** 60 | * Lists new updates (commits) to a repository. Note: performs a fetch (updates FETCH_HEAD). 61 | * @param path path where the repository resides. 62 | */ 63 | export async function listUpdates (path: string): Promise 64 | 65 | /** 66 | * Reads a repositorty's metadata (branch, revision, upstream). 67 | * @param path path where the repository resides. 68 | */ 69 | export async function readRepositoryMeta (path: string): Promise 70 | } 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@powercord/simple-git-wasm", 3 | "version": "0.2.0", 4 | "description": "A (relatively) small node library to clone and pull git repositories in a standalone manner thanks to libgit2, powered by WebAssembly and Emscripten", 5 | "repository": "git@github.com:powercord-org/simple-git-wasm.git", 6 | "author": "Cynthia ", 7 | "license": "BSD-3-Clause", 8 | "scripts": { 9 | "prepublish": "make build", 10 | "lint": "eslint src" 11 | }, 12 | "engines": { 13 | "node": ">= 14" 14 | }, 15 | "devDependencies": { 16 | "eslint": "^8.1.0" 17 | }, 18 | "exports": { 19 | ".": { 20 | "default": "./src/index.js", 21 | "types": "./index.d.ts" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /patches/0001-fix-permission-issues-with-nodefs.patch: -------------------------------------------------------------------------------- 1 | From 5d150a5aa2e552fe7c7851d04659fc45908b6f0a Mon Sep 17 00:00:00 2001 2 | From: Cynthia 3 | Date: Thu, 21 Oct 2021 22:36:15 +0200 4 | Subject: [PATCH 1/3] fix: permission issues with nodefs 5 | 6 | --- 7 | src/futils.c | 10 +++++----- 8 | src/odb.h | 2 +- 9 | src/pack.h | 2 +- 10 | 3 files changed, 7 insertions(+), 7 deletions(-) 11 | 12 | diff --git a/src/futils.c b/src/futils.c 13 | index a44820875..ecef08609 100644 14 | --- a/src/futils.c 15 | +++ b/src/futils.c 16 | @@ -41,11 +41,11 @@ int git_futils_mktmp(git_buf *path_out, const char *filename, mode_t mode) 17 | return -1; 18 | } 19 | 20 | - if (p_chmod(path_out->ptr, (mode & ~mask))) { 21 | - git_error_set(GIT_ERROR_OS, 22 | - "failed to set permissions on file '%s'", path_out->ptr); 23 | - return -1; 24 | - } 25 | + // if (p_chmod(path_out->ptr, (mode & ~mask))) { 26 | + // git_error_set(GIT_ERROR_OS, 27 | + // "failed to set permissions on file '%s'", path_out->ptr); 28 | + // return -1; 29 | + // } 30 | 31 | return fd; 32 | } 33 | diff --git a/src/odb.h b/src/odb.h 34 | index 4a8ebff19..b5d359e29 100644 35 | --- a/src/odb.h 36 | +++ b/src/odb.h 37 | @@ -22,7 +22,7 @@ 38 | 39 | #define GIT_OBJECTS_DIR "objects/" 40 | #define GIT_OBJECT_DIR_MODE 0777 41 | -#define GIT_OBJECT_FILE_MODE 0444 42 | +#define GIT_OBJECT_FILE_MODE 0644 43 | 44 | #define GIT_ODB_DEFAULT_LOOSE_PRIORITY 1 45 | #define GIT_ODB_DEFAULT_PACKED_PRIORITY 2 46 | diff --git a/src/pack.h b/src/pack.h 47 | index bf279c6b6..4c79657f8 100644 48 | --- a/src/pack.h 49 | +++ b/src/pack.h 50 | @@ -28,7 +28,7 @@ typedef int git_pack_foreach_entry_offset_cb( 51 | off64_t offset, 52 | void *payload); 53 | 54 | -#define GIT_PACK_FILE_MODE 0444 55 | +#define GIT_PACK_FILE_MODE 0644 56 | 57 | #define PACK_SIGNATURE 0x5041434b /* "PACK" */ 58 | #define PACK_VERSION 2 59 | -- 60 | 2.33.0 61 | 62 | -------------------------------------------------------------------------------- /patches/0002-chore-disable-error-reporting.patch: -------------------------------------------------------------------------------- 1 | From 81a6ae212cf2610a2015e18aee2434dca1d7359c Mon Sep 17 00:00:00 2001 2 | From: Cynthia 3 | Date: Thu, 21 Oct 2021 22:40:12 +0200 4 | Subject: [PATCH 2/3] chore: disable error reporting 5 | 6 | --- 7 | src/errors.c | 4 +- 8 | src/errors.h | 7 +- 9 | src/transports/http.c | 741 +----------------------------------------- 10 | 3 files changed, 8 insertions(+), 744 deletions(-) 11 | mode change 100644 => 120000 src/transports/http.c 12 | 13 | diff --git a/src/errors.c b/src/errors.c 14 | index ce883b2da..7209da22e 100644 15 | --- a/src/errors.c 16 | +++ b/src/errors.c 17 | @@ -55,7 +55,7 @@ void git_error_set_oom(void) 18 | GIT_THREADSTATE->last_error = &g_git_oom_error; 19 | } 20 | 21 | -void git_error_set(int error_class, const char *fmt, ...) 22 | +void _git_error_set(int error_class, const char *fmt, ...) 23 | { 24 | va_list ap; 25 | 26 | @@ -64,7 +64,7 @@ void git_error_set(int error_class, const char *fmt, ...) 27 | va_end(ap); 28 | } 29 | 30 | -void git_error_vset(int error_class, const char *fmt, va_list ap) 31 | +void _git_error_vset(int error_class, const char *fmt, va_list ap) 32 | { 33 | #ifdef GIT_WIN32 34 | DWORD win32_error_code = (error_class == GIT_ERROR_OS) ? GetLastError() : 0; 35 | diff --git a/src/errors.h b/src/errors.h 36 | index a2f60f752..6cdc14882 100644 37 | --- a/src/errors.h 38 | +++ b/src/errors.h 39 | @@ -8,13 +8,16 @@ 40 | #ifndef INCLUDE_errors_h__ 41 | #define INCLUDE_errors_h__ 42 | 43 | +#define git_error_set(...) 44 | +#define git_error_vset(...) 45 | + 46 | #include "common.h" 47 | 48 | /* 49 | * Set the error message for this thread, formatting as needed. 50 | */ 51 | -void git_error_set(int error_class, const char *fmt, ...) GIT_FORMAT_PRINTF(2, 3); 52 | -void git_error_vset(int error_class, const char *fmt, va_list ap); 53 | +void _git_error_set(int error_class, const char *fmt, ...) GIT_FORMAT_PRINTF(2, 3); 54 | +void _git_error_vset(int error_class, const char *fmt, va_list ap); 55 | 56 | /** 57 | * Set error message for user callback if needed. 58 | diff --git a/src/transports/http.c b/src/transports/http.c 59 | deleted file mode 100644 60 | index 914335aba..000000000 61 | --- a/src/transports/http.c 62 | +++ /dev/null 63 | @@ -1,740 +0,0 @@ 64 | -/* 65 | - * Copyright (C) the libgit2 contributors. All rights reserved. 66 | - * 67 | - * This file is part of libgit2, distributed under the GNU GPL v2 with 68 | - * a Linking Exception. For full terms see the included COPYING file. 69 | - */ 70 | - 71 | -#include "common.h" 72 | - 73 | -#ifndef GIT_WINHTTP 74 | - 75 | -#include "git2.h" 76 | -#include "http_parser.h" 77 | -#include "buffer.h" 78 | -#include "net.h" 79 | -#include "netops.h" 80 | -#include "remote.h" 81 | -#include "git2/sys/credential.h" 82 | -#include "smart.h" 83 | -#include "auth.h" 84 | -#include "http.h" 85 | -#include "auth_negotiate.h" 86 | -#include "auth_ntlm.h" 87 | -#include "trace.h" 88 | -#include "streams/tls.h" 89 | -#include "streams/socket.h" 90 | -#include "httpclient.h" 91 | - 92 | -bool git_http__expect_continue = false; 93 | - 94 | -typedef enum { 95 | - HTTP_STATE_NONE = 0, 96 | - HTTP_STATE_SENDING_REQUEST, 97 | - HTTP_STATE_RECEIVING_RESPONSE, 98 | - HTTP_STATE_DONE 99 | -} http_state; 100 | - 101 | -typedef struct { 102 | - git_http_method method; 103 | - const char *url; 104 | - const char *request_type; 105 | - const char *response_type; 106 | - unsigned chunked : 1; 107 | -} http_service; 108 | - 109 | -typedef struct { 110 | - git_smart_subtransport_stream parent; 111 | - const http_service *service; 112 | - http_state state; 113 | - unsigned replay_count; 114 | -} http_stream; 115 | - 116 | -typedef struct { 117 | - git_net_url url; 118 | - 119 | - git_credential *cred; 120 | - unsigned auth_schemetypes; 121 | - unsigned url_cred_presented : 1; 122 | -} http_server; 123 | - 124 | -typedef struct { 125 | - git_smart_subtransport parent; 126 | - transport_smart *owner; 127 | - 128 | - http_server server; 129 | - http_server proxy; 130 | - 131 | - git_http_client *http_client; 132 | -} http_subtransport; 133 | - 134 | -static const http_service upload_pack_ls_service = { 135 | - GIT_HTTP_METHOD_GET, "/info/refs?service=git-upload-pack", 136 | - NULL, 137 | - "application/x-git-upload-pack-advertisement", 138 | - 0 139 | -}; 140 | -static const http_service upload_pack_service = { 141 | - GIT_HTTP_METHOD_POST, "/git-upload-pack", 142 | - "application/x-git-upload-pack-request", 143 | - "application/x-git-upload-pack-result", 144 | - 0 145 | -}; 146 | -static const http_service receive_pack_ls_service = { 147 | - GIT_HTTP_METHOD_GET, "/info/refs?service=git-receive-pack", 148 | - NULL, 149 | - "application/x-git-receive-pack-advertisement", 150 | - 0 151 | -}; 152 | -static const http_service receive_pack_service = { 153 | - GIT_HTTP_METHOD_POST, "/git-receive-pack", 154 | - "application/x-git-receive-pack-request", 155 | - "application/x-git-receive-pack-result", 156 | - 1 157 | -}; 158 | - 159 | -#define SERVER_TYPE_REMOTE "remote" 160 | -#define SERVER_TYPE_PROXY "proxy" 161 | - 162 | -#define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport) 163 | - 164 | -static int apply_url_credentials( 165 | - git_credential **cred, 166 | - unsigned int allowed_types, 167 | - const char *username, 168 | - const char *password) 169 | -{ 170 | - GIT_ASSERT_ARG(username); 171 | - 172 | - if (!password) 173 | - password = ""; 174 | - 175 | - if (allowed_types & GIT_CREDENTIAL_USERPASS_PLAINTEXT) 176 | - return git_credential_userpass_plaintext_new(cred, username, password); 177 | - 178 | - if ((allowed_types & GIT_CREDENTIAL_DEFAULT) && *username == '\0' && *password == '\0') 179 | - return git_credential_default_new(cred); 180 | - 181 | - return GIT_PASSTHROUGH; 182 | -} 183 | - 184 | -GIT_INLINE(void) free_cred(git_credential **cred) 185 | -{ 186 | - if (*cred) { 187 | - git_credential_free(*cred); 188 | - (*cred) = NULL; 189 | - } 190 | -} 191 | - 192 | -static int handle_auth( 193 | - http_server *server, 194 | - const char *server_type, 195 | - const char *url, 196 | - unsigned int allowed_schemetypes, 197 | - unsigned int allowed_credtypes, 198 | - git_credential_acquire_cb callback, 199 | - void *callback_payload) 200 | -{ 201 | - int error = 1; 202 | - 203 | - if (server->cred) 204 | - free_cred(&server->cred); 205 | - 206 | - /* Start with URL-specified credentials, if there were any. */ 207 | - if ((allowed_credtypes & GIT_CREDENTIAL_USERPASS_PLAINTEXT) && 208 | - !server->url_cred_presented && 209 | - server->url.username) { 210 | - error = apply_url_credentials(&server->cred, allowed_credtypes, server->url.username, server->url.password); 211 | - server->url_cred_presented = 1; 212 | - 213 | - /* treat GIT_PASSTHROUGH as if callback isn't set */ 214 | - if (error == GIT_PASSTHROUGH) 215 | - error = 1; 216 | - } 217 | - 218 | - if (error > 0 && callback) { 219 | - error = callback(&server->cred, url, server->url.username, allowed_credtypes, callback_payload); 220 | - 221 | - /* treat GIT_PASSTHROUGH as if callback isn't set */ 222 | - if (error == GIT_PASSTHROUGH) 223 | - error = 1; 224 | - } 225 | - 226 | - if (error > 0) { 227 | - git_error_set(GIT_ERROR_HTTP, "%s authentication required but no callback set", server_type); 228 | - error = GIT_EAUTH; 229 | - } 230 | - 231 | - if (!error) 232 | - server->auth_schemetypes = allowed_schemetypes; 233 | - 234 | - return error; 235 | -} 236 | - 237 | -GIT_INLINE(int) handle_remote_auth( 238 | - http_stream *stream, 239 | - git_http_response *response) 240 | -{ 241 | - http_subtransport *transport = OWNING_SUBTRANSPORT(stream); 242 | - 243 | - if (response->server_auth_credtypes == 0) { 244 | - git_error_set(GIT_ERROR_HTTP, "server requires authentication that we do not support"); 245 | - return GIT_EAUTH; 246 | - } 247 | - 248 | - /* Otherwise, prompt for credentials. */ 249 | - return handle_auth( 250 | - &transport->server, 251 | - SERVER_TYPE_REMOTE, 252 | - transport->owner->url, 253 | - response->server_auth_schemetypes, 254 | - response->server_auth_credtypes, 255 | - transport->owner->cred_acquire_cb, 256 | - transport->owner->cred_acquire_payload); 257 | -} 258 | - 259 | -GIT_INLINE(int) handle_proxy_auth( 260 | - http_stream *stream, 261 | - git_http_response *response) 262 | -{ 263 | - http_subtransport *transport = OWNING_SUBTRANSPORT(stream); 264 | - 265 | - if (response->proxy_auth_credtypes == 0) { 266 | - git_error_set(GIT_ERROR_HTTP, "proxy requires authentication that we do not support"); 267 | - return GIT_EAUTH; 268 | - } 269 | - 270 | - /* Otherwise, prompt for credentials. */ 271 | - return handle_auth( 272 | - &transport->proxy, 273 | - SERVER_TYPE_PROXY, 274 | - transport->owner->proxy.url, 275 | - response->server_auth_schemetypes, 276 | - response->proxy_auth_credtypes, 277 | - transport->owner->proxy.credentials, 278 | - transport->owner->proxy.payload); 279 | -} 280 | - 281 | - 282 | -static int handle_response( 283 | - bool *complete, 284 | - http_stream *stream, 285 | - git_http_response *response, 286 | - bool allow_replay) 287 | -{ 288 | - http_subtransport *transport = OWNING_SUBTRANSPORT(stream); 289 | - int error; 290 | - 291 | - *complete = false; 292 | - 293 | - if (allow_replay && git_http_response_is_redirect(response)) { 294 | - if (!response->location) { 295 | - git_error_set(GIT_ERROR_HTTP, "redirect without location"); 296 | - return -1; 297 | - } 298 | - 299 | - if (git_net_url_apply_redirect(&transport->server.url, response->location, stream->service->url) < 0) { 300 | - return -1; 301 | - } 302 | - 303 | - return 0; 304 | - } else if (git_http_response_is_redirect(response)) { 305 | - git_error_set(GIT_ERROR_HTTP, "unexpected redirect"); 306 | - return -1; 307 | - } 308 | - 309 | - /* If we're in the middle of challenge/response auth, continue. */ 310 | - if (allow_replay && response->resend_credentials) { 311 | - return 0; 312 | - } else if (allow_replay && response->status == GIT_HTTP_STATUS_UNAUTHORIZED) { 313 | - if ((error = handle_remote_auth(stream, response)) < 0) 314 | - return error; 315 | - 316 | - return git_http_client_skip_body(transport->http_client); 317 | - } else if (allow_replay && response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) { 318 | - if ((error = handle_proxy_auth(stream, response)) < 0) 319 | - return error; 320 | - 321 | - return git_http_client_skip_body(transport->http_client); 322 | - } else if (response->status == GIT_HTTP_STATUS_UNAUTHORIZED || 323 | - response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) { 324 | - git_error_set(GIT_ERROR_HTTP, "unexpected authentication failure"); 325 | - return GIT_EAUTH; 326 | - } 327 | - 328 | - if (response->status != GIT_HTTP_STATUS_OK) { 329 | - git_error_set(GIT_ERROR_HTTP, "unexpected http status code: %d", response->status); 330 | - return -1; 331 | - } 332 | - 333 | - /* The response must contain a Content-Type header. */ 334 | - if (!response->content_type) { 335 | - git_error_set(GIT_ERROR_HTTP, "no content-type header in response"); 336 | - return -1; 337 | - } 338 | - 339 | - /* The Content-Type header must match our expectation. */ 340 | - if (strcmp(response->content_type, stream->service->response_type) != 0) { 341 | - git_error_set(GIT_ERROR_HTTP, "invalid content-type: '%s'", response->content_type); 342 | - return -1; 343 | - } 344 | - 345 | - *complete = true; 346 | - stream->state = HTTP_STATE_RECEIVING_RESPONSE; 347 | - return 0; 348 | -} 349 | - 350 | -static int lookup_proxy( 351 | - bool *out_use, 352 | - http_subtransport *transport) 353 | -{ 354 | - const char *proxy; 355 | - git_remote *remote; 356 | - char *config = NULL; 357 | - int error = 0; 358 | - 359 | - *out_use = false; 360 | - git_net_url_dispose(&transport->proxy.url); 361 | - 362 | - switch (transport->owner->proxy.type) { 363 | - case GIT_PROXY_SPECIFIED: 364 | - proxy = transport->owner->proxy.url; 365 | - break; 366 | - 367 | - case GIT_PROXY_AUTO: 368 | - remote = transport->owner->owner; 369 | - 370 | - error = git_remote__http_proxy(&config, remote, &transport->server.url); 371 | - 372 | - if (error || !config) 373 | - goto done; 374 | - 375 | - proxy = config; 376 | - break; 377 | - 378 | - default: 379 | - return 0; 380 | - } 381 | - 382 | - if (!proxy || 383 | - (error = git_net_url_parse(&transport->proxy.url, proxy)) < 0) 384 | - goto done; 385 | - 386 | - *out_use = true; 387 | - 388 | -done: 389 | - git__free(config); 390 | - return error; 391 | -} 392 | - 393 | -static int generate_request( 394 | - git_net_url *url, 395 | - git_http_request *request, 396 | - http_stream *stream, 397 | - size_t len) 398 | -{ 399 | - http_subtransport *transport = OWNING_SUBTRANSPORT(stream); 400 | - bool use_proxy = false; 401 | - int error; 402 | - 403 | - if ((error = git_net_url_joinpath(url, 404 | - &transport->server.url, stream->service->url)) < 0 || 405 | - (error = lookup_proxy(&use_proxy, transport)) < 0) 406 | - return error; 407 | - 408 | - request->method = stream->service->method; 409 | - request->url = url; 410 | - request->credentials = transport->server.cred; 411 | - request->proxy = use_proxy ? &transport->proxy.url : NULL; 412 | - request->proxy_credentials = transport->proxy.cred; 413 | - request->custom_headers = &transport->owner->custom_headers; 414 | - 415 | - if (stream->service->method == GIT_HTTP_METHOD_POST) { 416 | - request->chunked = stream->service->chunked; 417 | - request->content_length = stream->service->chunked ? 0 : len; 418 | - request->content_type = stream->service->request_type; 419 | - request->accept = stream->service->response_type; 420 | - request->expect_continue = git_http__expect_continue; 421 | - } 422 | - 423 | - return 0; 424 | -} 425 | - 426 | -/* 427 | - * Read from an HTTP transport - for the first invocation of this function 428 | - * (ie, when stream->state == HTTP_STATE_NONE), we'll send a GET request 429 | - * to the remote host. We will stream that data back on all subsequent 430 | - * calls. 431 | - */ 432 | -static int http_stream_read( 433 | - git_smart_subtransport_stream *s, 434 | - char *buffer, 435 | - size_t buffer_size, 436 | - size_t *out_len) 437 | -{ 438 | - http_stream *stream = (http_stream *)s; 439 | - http_subtransport *transport = OWNING_SUBTRANSPORT(stream); 440 | - git_net_url url = GIT_NET_URL_INIT; 441 | - git_net_url proxy_url = GIT_NET_URL_INIT; 442 | - git_http_request request = {0}; 443 | - git_http_response response = {0}; 444 | - bool complete; 445 | - int error; 446 | - 447 | - *out_len = 0; 448 | - 449 | - if (stream->state == HTTP_STATE_NONE) { 450 | - stream->state = HTTP_STATE_SENDING_REQUEST; 451 | - stream->replay_count = 0; 452 | - } 453 | - 454 | - /* 455 | - * Formulate the URL, send the request and read the response 456 | - * headers. Some of the request body may also be read. 457 | - */ 458 | - while (stream->state == HTTP_STATE_SENDING_REQUEST && 459 | - stream->replay_count < GIT_HTTP_REPLAY_MAX) { 460 | - git_net_url_dispose(&url); 461 | - git_net_url_dispose(&proxy_url); 462 | - git_http_response_dispose(&response); 463 | - 464 | - if ((error = generate_request(&url, &request, stream, 0)) < 0 || 465 | - (error = git_http_client_send_request( 466 | - transport->http_client, &request)) < 0 || 467 | - (error = git_http_client_read_response( 468 | - &response, transport->http_client)) < 0 || 469 | - (error = handle_response(&complete, stream, &response, true)) < 0) 470 | - goto done; 471 | - 472 | - if (complete) 473 | - break; 474 | - 475 | - stream->replay_count++; 476 | - } 477 | - 478 | - if (stream->state == HTTP_STATE_SENDING_REQUEST) { 479 | - git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays"); 480 | - error = GIT_ERROR; /* not GIT_EAUTH, because the exact cause is unclear */ 481 | - goto done; 482 | - } 483 | - 484 | - GIT_ASSERT(stream->state == HTTP_STATE_RECEIVING_RESPONSE); 485 | - 486 | - error = git_http_client_read_body(transport->http_client, buffer, buffer_size); 487 | - 488 | - if (error > 0) { 489 | - *out_len = error; 490 | - error = 0; 491 | - } 492 | - 493 | -done: 494 | - git_net_url_dispose(&url); 495 | - git_net_url_dispose(&proxy_url); 496 | - git_http_response_dispose(&response); 497 | - 498 | - return error; 499 | -} 500 | - 501 | -static bool needs_probe(http_stream *stream) 502 | -{ 503 | - http_subtransport *transport = OWNING_SUBTRANSPORT(stream); 504 | - 505 | - return (transport->server.auth_schemetypes == GIT_HTTP_AUTH_NTLM || 506 | - transport->server.auth_schemetypes == GIT_HTTP_AUTH_NEGOTIATE); 507 | -} 508 | - 509 | -static int send_probe(http_stream *stream) 510 | -{ 511 | - http_subtransport *transport = OWNING_SUBTRANSPORT(stream); 512 | - git_http_client *client = transport->http_client; 513 | - const char *probe = "0000"; 514 | - size_t len = 4; 515 | - git_net_url url = GIT_NET_URL_INIT; 516 | - git_http_request request = {0}; 517 | - git_http_response response = {0}; 518 | - bool complete = false; 519 | - size_t step, steps = 1; 520 | - int error; 521 | - 522 | - /* NTLM requires a full challenge/response */ 523 | - if (transport->server.auth_schemetypes == GIT_HTTP_AUTH_NTLM) 524 | - steps = GIT_AUTH_STEPS_NTLM; 525 | - 526 | - /* 527 | - * Send at most two requests: one without any authentication to see 528 | - * if we get prompted to authenticate. If we do, send a second one 529 | - * with the first authentication message. The final authentication 530 | - * message with the response will occur with the *actual* POST data. 531 | - */ 532 | - for (step = 0; step < steps && !complete; step++) { 533 | - git_net_url_dispose(&url); 534 | - git_http_response_dispose(&response); 535 | - 536 | - if ((error = generate_request(&url, &request, stream, len)) < 0 || 537 | - (error = git_http_client_send_request(client, &request)) < 0 || 538 | - (error = git_http_client_send_body(client, probe, len)) < 0 || 539 | - (error = git_http_client_read_response(&response, client)) < 0 || 540 | - (error = git_http_client_skip_body(client)) < 0 || 541 | - (error = handle_response(&complete, stream, &response, true)) < 0) 542 | - goto done; 543 | - } 544 | - 545 | -done: 546 | - git_http_response_dispose(&response); 547 | - git_net_url_dispose(&url); 548 | - return error; 549 | -} 550 | - 551 | -/* 552 | -* Write to an HTTP transport - for the first invocation of this function 553 | -* (ie, when stream->state == HTTP_STATE_NONE), we'll send a POST request 554 | -* to the remote host. If we're sending chunked data, then subsequent calls 555 | -* will write the additional data given in the buffer. If we're not chunking, 556 | -* then the caller should have given us all the data in the original call. 557 | -* The caller should call http_stream_read_response to get the result. 558 | -*/ 559 | -static int http_stream_write( 560 | - git_smart_subtransport_stream *s, 561 | - const char *buffer, 562 | - size_t len) 563 | -{ 564 | - http_stream *stream = GIT_CONTAINER_OF(s, http_stream, parent); 565 | - http_subtransport *transport = OWNING_SUBTRANSPORT(stream); 566 | - git_net_url url = GIT_NET_URL_INIT; 567 | - git_http_request request = {0}; 568 | - git_http_response response = {0}; 569 | - int error; 570 | - 571 | - while (stream->state == HTTP_STATE_NONE && 572 | - stream->replay_count < GIT_HTTP_REPLAY_MAX) { 573 | - 574 | - git_net_url_dispose(&url); 575 | - git_http_response_dispose(&response); 576 | - 577 | - /* 578 | - * If we're authenticating with a connection-based mechanism 579 | - * (NTLM, Kerberos), send a "probe" packet. Servers SHOULD 580 | - * authenticate an entire keep-alive connection, so ideally 581 | - * we should not need to authenticate but some servers do 582 | - * not support this. By sending a probe packet, we'll be 583 | - * able to follow up with a second POST using the actual 584 | - * data (and, in the degenerate case, the authentication 585 | - * header as well). 586 | - */ 587 | - if (needs_probe(stream) && (error = send_probe(stream)) < 0) 588 | - goto done; 589 | - 590 | - /* Send the regular POST request. */ 591 | - if ((error = generate_request(&url, &request, stream, len)) < 0 || 592 | - (error = git_http_client_send_request( 593 | - transport->http_client, &request)) < 0) 594 | - goto done; 595 | - 596 | - if (request.expect_continue && 597 | - git_http_client_has_response(transport->http_client)) { 598 | - bool complete; 599 | - 600 | - /* 601 | - * If we got a response to an expect/continue, then 602 | - * it's something other than a 100 and we should 603 | - * deal with the response somehow. 604 | - */ 605 | - if ((error = git_http_client_read_response(&response, transport->http_client)) < 0 || 606 | - (error = handle_response(&complete, stream, &response, true)) < 0) 607 | - goto done; 608 | - } else { 609 | - stream->state = HTTP_STATE_SENDING_REQUEST; 610 | - } 611 | - 612 | - stream->replay_count++; 613 | - } 614 | - 615 | - if (stream->state == HTTP_STATE_NONE) { 616 | - git_error_set(GIT_ERROR_HTTP, 617 | - "too many redirects or authentication replays"); 618 | - error = GIT_ERROR; /* not GIT_EAUTH because the exact cause is unclear */ 619 | - goto done; 620 | - } 621 | - 622 | - GIT_ASSERT(stream->state == HTTP_STATE_SENDING_REQUEST); 623 | - 624 | - error = git_http_client_send_body(transport->http_client, buffer, len); 625 | - 626 | -done: 627 | - git_http_response_dispose(&response); 628 | - git_net_url_dispose(&url); 629 | - return error; 630 | -} 631 | - 632 | -/* 633 | -* Read from an HTTP transport after it has been written to. This is the 634 | -* response from a POST request made by http_stream_write. 635 | -*/ 636 | -static int http_stream_read_response( 637 | - git_smart_subtransport_stream *s, 638 | - char *buffer, 639 | - size_t buffer_size, 640 | - size_t *out_len) 641 | -{ 642 | - http_stream *stream = (http_stream *)s; 643 | - http_subtransport *transport = OWNING_SUBTRANSPORT(stream); 644 | - git_http_client *client = transport->http_client; 645 | - git_http_response response = {0}; 646 | - bool complete; 647 | - int error; 648 | - 649 | - *out_len = 0; 650 | - 651 | - if (stream->state == HTTP_STATE_SENDING_REQUEST) { 652 | - if ((error = git_http_client_read_response(&response, client)) < 0 || 653 | - (error = handle_response(&complete, stream, &response, false)) < 0) 654 | - goto done; 655 | - 656 | - GIT_ASSERT(complete); 657 | - stream->state = HTTP_STATE_RECEIVING_RESPONSE; 658 | - } 659 | - 660 | - error = git_http_client_read_body(client, buffer, buffer_size); 661 | - 662 | - if (error > 0) { 663 | - *out_len = error; 664 | - error = 0; 665 | - } 666 | - 667 | -done: 668 | - git_http_response_dispose(&response); 669 | - return error; 670 | -} 671 | - 672 | -static void http_stream_free(git_smart_subtransport_stream *stream) 673 | -{ 674 | - http_stream *s = GIT_CONTAINER_OF(stream, http_stream, parent); 675 | - git__free(s); 676 | -} 677 | - 678 | -static const http_service *select_service(git_smart_service_t action) 679 | -{ 680 | - switch (action) { 681 | - case GIT_SERVICE_UPLOADPACK_LS: 682 | - return &upload_pack_ls_service; 683 | - case GIT_SERVICE_UPLOADPACK: 684 | - return &upload_pack_service; 685 | - case GIT_SERVICE_RECEIVEPACK_LS: 686 | - return &receive_pack_ls_service; 687 | - case GIT_SERVICE_RECEIVEPACK: 688 | - return &receive_pack_service; 689 | - } 690 | - 691 | - return NULL; 692 | -} 693 | - 694 | -static int http_action( 695 | - git_smart_subtransport_stream **out, 696 | - git_smart_subtransport *t, 697 | - const char *url, 698 | - git_smart_service_t action) 699 | -{ 700 | - http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent); 701 | - http_stream *stream; 702 | - const http_service *service; 703 | - int error; 704 | - 705 | - GIT_ASSERT_ARG(out); 706 | - GIT_ASSERT_ARG(t); 707 | - 708 | - *out = NULL; 709 | - 710 | - /* 711 | - * If we've seen a redirect then preserve the location that we've 712 | - * been given. This is important to continue authorization against 713 | - * the redirect target, not the user-given source; the endpoint may 714 | - * have redirected us from HTTP->HTTPS and is using an auth mechanism 715 | - * that would be insecure in plaintext (eg, HTTP Basic). 716 | - */ 717 | - if (!git_net_url_valid(&transport->server.url) && 718 | - (error = git_net_url_parse(&transport->server.url, url)) < 0) 719 | - return error; 720 | - 721 | - if ((service = select_service(action)) == NULL) { 722 | - git_error_set(GIT_ERROR_HTTP, "invalid action"); 723 | - return -1; 724 | - } 725 | - 726 | - stream = git__calloc(sizeof(http_stream), 1); 727 | - GIT_ERROR_CHECK_ALLOC(stream); 728 | - 729 | - if (!transport->http_client) { 730 | - git_http_client_options opts = {0}; 731 | - 732 | - opts.server_certificate_check_cb = transport->owner->certificate_check_cb; 733 | - opts.server_certificate_check_payload = transport->owner->message_cb_payload; 734 | - opts.proxy_certificate_check_cb = transport->owner->proxy.certificate_check; 735 | - opts.proxy_certificate_check_payload = transport->owner->proxy.payload; 736 | - 737 | - if (git_http_client_new(&transport->http_client, &opts) < 0) 738 | - return -1; 739 | - } 740 | - 741 | - stream->service = service; 742 | - stream->parent.subtransport = &transport->parent; 743 | - 744 | - if (service->method == GIT_HTTP_METHOD_GET) { 745 | - stream->parent.read = http_stream_read; 746 | - } else { 747 | - stream->parent.write = http_stream_write; 748 | - stream->parent.read = http_stream_read_response; 749 | - } 750 | - 751 | - stream->parent.free = http_stream_free; 752 | - 753 | - *out = (git_smart_subtransport_stream *)stream; 754 | - return 0; 755 | -} 756 | - 757 | -static int http_close(git_smart_subtransport *t) 758 | -{ 759 | - http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent); 760 | - 761 | - free_cred(&transport->server.cred); 762 | - free_cred(&transport->proxy.cred); 763 | - 764 | - transport->server.url_cred_presented = false; 765 | - transport->proxy.url_cred_presented = false; 766 | - 767 | - git_net_url_dispose(&transport->server.url); 768 | - git_net_url_dispose(&transport->proxy.url); 769 | - 770 | - return 0; 771 | -} 772 | - 773 | -static void http_free(git_smart_subtransport *t) 774 | -{ 775 | - http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent); 776 | - 777 | - git_http_client_free(transport->http_client); 778 | - 779 | - http_close(t); 780 | - git__free(transport); 781 | -} 782 | - 783 | -int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param) 784 | -{ 785 | - http_subtransport *transport; 786 | - 787 | - GIT_UNUSED(param); 788 | - 789 | - GIT_ASSERT_ARG(out); 790 | - 791 | - transport = git__calloc(sizeof(http_subtransport), 1); 792 | - GIT_ERROR_CHECK_ALLOC(transport); 793 | - 794 | - transport->owner = (transport_smart *)owner; 795 | - transport->parent.action = http_action; 796 | - transport->parent.close = http_close; 797 | - transport->parent.free = http_free; 798 | - 799 | - *out = (git_smart_subtransport *) transport; 800 | - return 0; 801 | -} 802 | - 803 | -#endif /* !GIT_WINHTTP */ 804 | diff --git a/src/transports/http.c b/src/transports/http.c 805 | new file mode 120000 806 | index 000000000..67a1e072f 807 | --- /dev/null 808 | +++ b/src/transports/http.c 809 | @@ -0,0 +1 @@ 810 | +../../../src/lib/http.c 811 | \ No newline at end of file 812 | -- 813 | 2.33.0 814 | 815 | -------------------------------------------------------------------------------- /patches/0003-chore-remove-file-and-line-info.patch: -------------------------------------------------------------------------------- 1 | From 2f20a6facf5a219b27e1c7cb35a76961667671f4 Mon Sep 17 00:00:00 2001 2 | From: Cynthia 3 | Date: Thu, 21 Oct 2021 22:42:35 +0200 4 | Subject: [PATCH 3/3] chore: remove file and line info 5 | 6 | --- 7 | src/alloc.h | 16 ++++++++-------- 8 | 1 file changed, 8 insertions(+), 8 deletions(-) 9 | 10 | diff --git a/src/alloc.h b/src/alloc.h 11 | index 04fb7e101..8042a7ff7 100644 12 | --- a/src/alloc.h 13 | +++ b/src/alloc.h 14 | @@ -12,14 +12,14 @@ 15 | 16 | extern git_allocator git__allocator; 17 | 18 | -#define git__malloc(len) git__allocator.gmalloc(len, __FILE__, __LINE__) 19 | -#define git__calloc(nelem, elsize) git__allocator.gcalloc(nelem, elsize, __FILE__, __LINE__) 20 | -#define git__strdup(str) git__allocator.gstrdup(str, __FILE__, __LINE__) 21 | -#define git__strndup(str, n) git__allocator.gstrndup(str, n, __FILE__, __LINE__) 22 | -#define git__substrdup(str, n) git__allocator.gsubstrdup(str, n, __FILE__, __LINE__) 23 | -#define git__realloc(ptr, size) git__allocator.grealloc(ptr, size, __FILE__, __LINE__) 24 | -#define git__reallocarray(ptr, nelem, elsize) git__allocator.greallocarray(ptr, nelem, elsize, __FILE__, __LINE__) 25 | -#define git__mallocarray(nelem, elsize) git__allocator.gmallocarray(nelem, elsize, __FILE__, __LINE__) 26 | +#define git__malloc(len) git__allocator.gmalloc(len, "lg2", 0) 27 | +#define git__calloc(nelem, elsize) git__allocator.gcalloc(nelem, elsize, "lg2", 0) 28 | +#define git__strdup(str) git__allocator.gstrdup(str, "lg2", 0) 29 | +#define git__strndup(str, n) git__allocator.gstrndup(str, n, "lg2", 0) 30 | +#define git__substrdup(str, n) git__allocator.gsubstrdup(str, n, "lg2", 0) 31 | +#define git__realloc(ptr, size) git__allocator.grealloc(ptr, size, "lg2", 0) 32 | +#define git__reallocarray(ptr, nelem, elsize) git__allocator.greallocarray(ptr, nelem, elsize, "lg2", 0) 33 | +#define git__mallocarray(nelem, elsize) git__allocator.gmallocarray(nelem, elsize, "lg2", 0) 34 | #define git__free git__allocator.gfree 35 | 36 | /** 37 | -- 38 | 2.33.0 39 | 40 | -------------------------------------------------------------------------------- /patches/0004-chore-ssh-allow-for-fake-ssh-support.patch: -------------------------------------------------------------------------------- 1 | From 522098dd93713c0908bab3d421dc5681809a6252 Mon Sep 17 00:00:00 2001 2 | From: Cynthia 3 | Date: Fri, 29 Oct 2021 19:42:47 +0200 4 | Subject: [PATCH] chore(ssh): allow for fake ssh support 5 | via http transport 6 | 7 | --- 8 | src/transport.c | 12 +++++------- 9 | 1 file changed, 5 insertions(+), 7 deletions(-) 10 | 11 | diff --git a/src/transport.c b/src/transport.c 12 | index e128aa6c7..731cb626f 100644 13 | --- a/src/transport.c 14 | +++ b/src/transport.c 15 | @@ -21,23 +21,21 @@ typedef struct transport_definition { 16 | } transport_definition; 17 | 18 | static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1, NULL }; 19 | -static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0, NULL }; 20 | -#ifdef GIT_SSH 21 | -static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0, NULL }; 22 | -#endif 23 | +// static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0, NULL }; 24 | +static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 1, NULL }; 25 | 26 | static transport_definition local_transport_definition = { "file://", git_transport_local, NULL }; 27 | 28 | static transport_definition transports[] = { 29 | - { "git://", git_transport_smart, &git_subtransport_definition }, 30 | +// { "git://", git_transport_smart, &git_subtransport_definition }, 31 | { "http://", git_transport_smart, &http_subtransport_definition }, 32 | { "https://", git_transport_smart, &http_subtransport_definition }, 33 | { "file://", git_transport_local, NULL }, 34 | -#ifdef GIT_SSH 35 | +// #ifdef GIT_SSH 36 | { "ssh://", git_transport_smart, &ssh_subtransport_definition }, 37 | { "ssh+git://", git_transport_smart, &ssh_subtransport_definition }, 38 | { "git+ssh://", git_transport_smart, &ssh_subtransport_definition }, 39 | -#endif 40 | +// #endif 41 | { NULL, 0, 0 } 42 | }; 43 | 44 | -- 45 | 2.33.0 46 | 47 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.3 2 | 3 | specifiers: 4 | eslint: ^8.1.0 5 | 6 | devDependencies: 7 | eslint: 8.1.0 8 | 9 | packages: 10 | 11 | /@eslint/eslintrc/1.0.3: 12 | resolution: {integrity: sha512-DHI1wDPoKCBPoLZA3qDR91+3te/wDSc1YhKg3jR8NxKKRJq2hwHwcWv31cSwSYvIBrmbENoYMWcenW8uproQqg==} 13 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 14 | dependencies: 15 | ajv: 6.12.6 16 | debug: 4.3.2 17 | espree: 9.0.0 18 | globals: 13.10.0 19 | ignore: 4.0.6 20 | import-fresh: 3.3.0 21 | js-yaml: 3.14.1 22 | minimatch: 3.0.4 23 | strip-json-comments: 3.1.1 24 | transitivePeerDependencies: 25 | - supports-color 26 | dev: true 27 | 28 | /@humanwhocodes/config-array/0.6.0: 29 | resolution: {integrity: sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==} 30 | engines: {node: '>=10.10.0'} 31 | dependencies: 32 | '@humanwhocodes/object-schema': 1.2.0 33 | debug: 4.3.2 34 | minimatch: 3.0.4 35 | transitivePeerDependencies: 36 | - supports-color 37 | dev: true 38 | 39 | /@humanwhocodes/object-schema/1.2.0: 40 | resolution: {integrity: sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==} 41 | dev: true 42 | 43 | /acorn-jsx/5.3.2_acorn@8.5.0: 44 | resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 45 | peerDependencies: 46 | acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 47 | dependencies: 48 | acorn: 8.5.0 49 | dev: true 50 | 51 | /acorn/8.5.0: 52 | resolution: {integrity: sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==} 53 | engines: {node: '>=0.4.0'} 54 | hasBin: true 55 | dev: true 56 | 57 | /ajv/6.12.6: 58 | resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 59 | dependencies: 60 | fast-deep-equal: 3.1.3 61 | fast-json-stable-stringify: 2.1.0 62 | json-schema-traverse: 0.4.1 63 | uri-js: 4.4.1 64 | dev: true 65 | 66 | /ansi-colors/4.1.1: 67 | resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} 68 | engines: {node: '>=6'} 69 | dev: true 70 | 71 | /ansi-regex/5.0.0: 72 | resolution: {integrity: sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==} 73 | engines: {node: '>=8'} 74 | dev: true 75 | 76 | /ansi-styles/4.3.0: 77 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 78 | engines: {node: '>=8'} 79 | dependencies: 80 | color-convert: 2.0.1 81 | dev: true 82 | 83 | /argparse/1.0.10: 84 | resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} 85 | dependencies: 86 | sprintf-js: 1.0.3 87 | dev: true 88 | 89 | /argparse/2.0.1: 90 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 91 | dev: true 92 | 93 | /balanced-match/1.0.2: 94 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 95 | dev: true 96 | 97 | /brace-expansion/1.1.11: 98 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 99 | dependencies: 100 | balanced-match: 1.0.2 101 | concat-map: 0.0.1 102 | dev: true 103 | 104 | /callsites/3.1.0: 105 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 106 | engines: {node: '>=6'} 107 | dev: true 108 | 109 | /chalk/4.1.1: 110 | resolution: {integrity: sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==} 111 | engines: {node: '>=10'} 112 | dependencies: 113 | ansi-styles: 4.3.0 114 | supports-color: 7.2.0 115 | dev: true 116 | 117 | /color-convert/2.0.1: 118 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 119 | engines: {node: '>=7.0.0'} 120 | dependencies: 121 | color-name: 1.1.4 122 | dev: true 123 | 124 | /color-name/1.1.4: 125 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 126 | dev: true 127 | 128 | /concat-map/0.0.1: 129 | resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} 130 | dev: true 131 | 132 | /cross-spawn/7.0.3: 133 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 134 | engines: {node: '>= 8'} 135 | dependencies: 136 | path-key: 3.1.1 137 | shebang-command: 2.0.0 138 | which: 2.0.2 139 | dev: true 140 | 141 | /debug/4.3.2: 142 | resolution: {integrity: sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==} 143 | engines: {node: '>=6.0'} 144 | peerDependencies: 145 | supports-color: '*' 146 | peerDependenciesMeta: 147 | supports-color: 148 | optional: true 149 | dependencies: 150 | ms: 2.1.2 151 | dev: true 152 | 153 | /deep-is/0.1.3: 154 | resolution: {integrity: sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=} 155 | dev: true 156 | 157 | /doctrine/3.0.0: 158 | resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} 159 | engines: {node: '>=6.0.0'} 160 | dependencies: 161 | esutils: 2.0.3 162 | dev: true 163 | 164 | /enquirer/2.3.6: 165 | resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} 166 | engines: {node: '>=8.6'} 167 | dependencies: 168 | ansi-colors: 4.1.1 169 | dev: true 170 | 171 | /escape-string-regexp/4.0.0: 172 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 173 | engines: {node: '>=10'} 174 | dev: true 175 | 176 | /eslint-scope/6.0.0: 177 | resolution: {integrity: sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA==} 178 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 179 | dependencies: 180 | esrecurse: 4.3.0 181 | estraverse: 5.2.0 182 | dev: true 183 | 184 | /eslint-utils/3.0.0_eslint@8.1.0: 185 | resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} 186 | engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} 187 | peerDependencies: 188 | eslint: '>=5' 189 | dependencies: 190 | eslint: 8.1.0 191 | eslint-visitor-keys: 2.1.0 192 | dev: true 193 | 194 | /eslint-visitor-keys/2.1.0: 195 | resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} 196 | engines: {node: '>=10'} 197 | dev: true 198 | 199 | /eslint-visitor-keys/3.0.0: 200 | resolution: {integrity: sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==} 201 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 202 | dev: true 203 | 204 | /eslint/8.1.0: 205 | resolution: {integrity: sha512-JZvNneArGSUsluHWJ8g8MMs3CfIEzwaLx9KyH4tZ2i+R2/rPWzL8c0zg3rHdwYVpN/1sB9gqnjHwz9HoeJpGHw==} 206 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 207 | hasBin: true 208 | dependencies: 209 | '@eslint/eslintrc': 1.0.3 210 | '@humanwhocodes/config-array': 0.6.0 211 | ajv: 6.12.6 212 | chalk: 4.1.1 213 | cross-spawn: 7.0.3 214 | debug: 4.3.2 215 | doctrine: 3.0.0 216 | enquirer: 2.3.6 217 | escape-string-regexp: 4.0.0 218 | eslint-scope: 6.0.0 219 | eslint-utils: 3.0.0_eslint@8.1.0 220 | eslint-visitor-keys: 3.0.0 221 | espree: 9.0.0 222 | esquery: 1.4.0 223 | esutils: 2.0.3 224 | fast-deep-equal: 3.1.3 225 | file-entry-cache: 6.0.1 226 | functional-red-black-tree: 1.0.1 227 | glob-parent: 6.0.2 228 | globals: 13.10.0 229 | ignore: 4.0.6 230 | import-fresh: 3.3.0 231 | imurmurhash: 0.1.4 232 | is-glob: 4.0.3 233 | js-yaml: 4.1.0 234 | json-stable-stringify-without-jsonify: 1.0.1 235 | levn: 0.4.1 236 | lodash.merge: 4.6.2 237 | minimatch: 3.0.4 238 | natural-compare: 1.4.0 239 | optionator: 0.9.1 240 | progress: 2.0.3 241 | regexpp: 3.2.0 242 | semver: 7.3.5 243 | strip-ansi: 6.0.0 244 | strip-json-comments: 3.1.1 245 | text-table: 0.2.0 246 | v8-compile-cache: 2.3.0 247 | transitivePeerDependencies: 248 | - supports-color 249 | dev: true 250 | 251 | /espree/9.0.0: 252 | resolution: {integrity: sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==} 253 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 254 | dependencies: 255 | acorn: 8.5.0 256 | acorn-jsx: 5.3.2_acorn@8.5.0 257 | eslint-visitor-keys: 3.0.0 258 | dev: true 259 | 260 | /esprima/4.0.1: 261 | resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} 262 | engines: {node: '>=4'} 263 | hasBin: true 264 | dev: true 265 | 266 | /esquery/1.4.0: 267 | resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} 268 | engines: {node: '>=0.10'} 269 | dependencies: 270 | estraverse: 5.2.0 271 | dev: true 272 | 273 | /esrecurse/4.3.0: 274 | resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 275 | engines: {node: '>=4.0'} 276 | dependencies: 277 | estraverse: 5.2.0 278 | dev: true 279 | 280 | /estraverse/5.2.0: 281 | resolution: {integrity: sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==} 282 | engines: {node: '>=4.0'} 283 | dev: true 284 | 285 | /esutils/2.0.3: 286 | resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 287 | engines: {node: '>=0.10.0'} 288 | dev: true 289 | 290 | /fast-deep-equal/3.1.3: 291 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 292 | dev: true 293 | 294 | /fast-json-stable-stringify/2.1.0: 295 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 296 | dev: true 297 | 298 | /fast-levenshtein/2.0.6: 299 | resolution: {integrity: sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=} 300 | dev: true 301 | 302 | /file-entry-cache/6.0.1: 303 | resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} 304 | engines: {node: ^10.12.0 || >=12.0.0} 305 | dependencies: 306 | flat-cache: 3.0.4 307 | dev: true 308 | 309 | /flat-cache/3.0.4: 310 | resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} 311 | engines: {node: ^10.12.0 || >=12.0.0} 312 | dependencies: 313 | flatted: 3.2.1 314 | rimraf: 3.0.2 315 | dev: true 316 | 317 | /flatted/3.2.1: 318 | resolution: {integrity: sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==} 319 | dev: true 320 | 321 | /fs.realpath/1.0.0: 322 | resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=} 323 | dev: true 324 | 325 | /functional-red-black-tree/1.0.1: 326 | resolution: {integrity: sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=} 327 | dev: true 328 | 329 | /glob-parent/6.0.2: 330 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 331 | engines: {node: '>=10.13.0'} 332 | dependencies: 333 | is-glob: 4.0.3 334 | dev: true 335 | 336 | /glob/7.1.7: 337 | resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} 338 | dependencies: 339 | fs.realpath: 1.0.0 340 | inflight: 1.0.6 341 | inherits: 2.0.4 342 | minimatch: 3.0.4 343 | once: 1.4.0 344 | path-is-absolute: 1.0.1 345 | dev: true 346 | 347 | /globals/13.10.0: 348 | resolution: {integrity: sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==} 349 | engines: {node: '>=8'} 350 | dependencies: 351 | type-fest: 0.20.2 352 | dev: true 353 | 354 | /has-flag/4.0.0: 355 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 356 | engines: {node: '>=8'} 357 | dev: true 358 | 359 | /ignore/4.0.6: 360 | resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} 361 | engines: {node: '>= 4'} 362 | dev: true 363 | 364 | /import-fresh/3.3.0: 365 | resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} 366 | engines: {node: '>=6'} 367 | dependencies: 368 | parent-module: 1.0.1 369 | resolve-from: 4.0.0 370 | dev: true 371 | 372 | /imurmurhash/0.1.4: 373 | resolution: {integrity: sha1-khi5srkoojixPcT7a21XbyMUU+o=} 374 | engines: {node: '>=0.8.19'} 375 | dev: true 376 | 377 | /inflight/1.0.6: 378 | resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=} 379 | dependencies: 380 | once: 1.4.0 381 | wrappy: 1.0.2 382 | dev: true 383 | 384 | /inherits/2.0.4: 385 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 386 | dev: true 387 | 388 | /is-extglob/2.1.1: 389 | resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=} 390 | engines: {node: '>=0.10.0'} 391 | dev: true 392 | 393 | /is-glob/4.0.3: 394 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 395 | engines: {node: '>=0.10.0'} 396 | dependencies: 397 | is-extglob: 2.1.1 398 | dev: true 399 | 400 | /isexe/2.0.0: 401 | resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=} 402 | dev: true 403 | 404 | /js-yaml/3.14.1: 405 | resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} 406 | hasBin: true 407 | dependencies: 408 | argparse: 1.0.10 409 | esprima: 4.0.1 410 | dev: true 411 | 412 | /js-yaml/4.1.0: 413 | resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 414 | hasBin: true 415 | dependencies: 416 | argparse: 2.0.1 417 | dev: true 418 | 419 | /json-schema-traverse/0.4.1: 420 | resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 421 | dev: true 422 | 423 | /json-stable-stringify-without-jsonify/1.0.1: 424 | resolution: {integrity: sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=} 425 | dev: true 426 | 427 | /levn/0.4.1: 428 | resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 429 | engines: {node: '>= 0.8.0'} 430 | dependencies: 431 | prelude-ls: 1.2.1 432 | type-check: 0.4.0 433 | dev: true 434 | 435 | /lodash.merge/4.6.2: 436 | resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 437 | dev: true 438 | 439 | /lru-cache/6.0.0: 440 | resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} 441 | engines: {node: '>=10'} 442 | dependencies: 443 | yallist: 4.0.0 444 | dev: true 445 | 446 | /minimatch/3.0.4: 447 | resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} 448 | dependencies: 449 | brace-expansion: 1.1.11 450 | dev: true 451 | 452 | /ms/2.1.2: 453 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 454 | dev: true 455 | 456 | /natural-compare/1.4.0: 457 | resolution: {integrity: sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=} 458 | dev: true 459 | 460 | /once/1.4.0: 461 | resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} 462 | dependencies: 463 | wrappy: 1.0.2 464 | dev: true 465 | 466 | /optionator/0.9.1: 467 | resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} 468 | engines: {node: '>= 0.8.0'} 469 | dependencies: 470 | deep-is: 0.1.3 471 | fast-levenshtein: 2.0.6 472 | levn: 0.4.1 473 | prelude-ls: 1.2.1 474 | type-check: 0.4.0 475 | word-wrap: 1.2.3 476 | dev: true 477 | 478 | /parent-module/1.0.1: 479 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 480 | engines: {node: '>=6'} 481 | dependencies: 482 | callsites: 3.1.0 483 | dev: true 484 | 485 | /path-is-absolute/1.0.1: 486 | resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} 487 | engines: {node: '>=0.10.0'} 488 | dev: true 489 | 490 | /path-key/3.1.1: 491 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 492 | engines: {node: '>=8'} 493 | dev: true 494 | 495 | /prelude-ls/1.2.1: 496 | resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 497 | engines: {node: '>= 0.8.0'} 498 | dev: true 499 | 500 | /progress/2.0.3: 501 | resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} 502 | engines: {node: '>=0.4.0'} 503 | dev: true 504 | 505 | /punycode/2.1.1: 506 | resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} 507 | engines: {node: '>=6'} 508 | dev: true 509 | 510 | /regexpp/3.2.0: 511 | resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} 512 | engines: {node: '>=8'} 513 | dev: true 514 | 515 | /resolve-from/4.0.0: 516 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 517 | engines: {node: '>=4'} 518 | dev: true 519 | 520 | /rimraf/3.0.2: 521 | resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} 522 | hasBin: true 523 | dependencies: 524 | glob: 7.1.7 525 | dev: true 526 | 527 | /semver/7.3.5: 528 | resolution: {integrity: sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==} 529 | engines: {node: '>=10'} 530 | hasBin: true 531 | dependencies: 532 | lru-cache: 6.0.0 533 | dev: true 534 | 535 | /shebang-command/2.0.0: 536 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 537 | engines: {node: '>=8'} 538 | dependencies: 539 | shebang-regex: 3.0.0 540 | dev: true 541 | 542 | /shebang-regex/3.0.0: 543 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 544 | engines: {node: '>=8'} 545 | dev: true 546 | 547 | /sprintf-js/1.0.3: 548 | resolution: {integrity: sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=} 549 | dev: true 550 | 551 | /strip-ansi/6.0.0: 552 | resolution: {integrity: sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==} 553 | engines: {node: '>=8'} 554 | dependencies: 555 | ansi-regex: 5.0.0 556 | dev: true 557 | 558 | /strip-json-comments/3.1.1: 559 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 560 | engines: {node: '>=8'} 561 | dev: true 562 | 563 | /supports-color/7.2.0: 564 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 565 | engines: {node: '>=8'} 566 | dependencies: 567 | has-flag: 4.0.0 568 | dev: true 569 | 570 | /text-table/0.2.0: 571 | resolution: {integrity: sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=} 572 | dev: true 573 | 574 | /type-check/0.4.0: 575 | resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 576 | engines: {node: '>= 0.8.0'} 577 | dependencies: 578 | prelude-ls: 1.2.1 579 | dev: true 580 | 581 | /type-fest/0.20.2: 582 | resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} 583 | engines: {node: '>=10'} 584 | dev: true 585 | 586 | /uri-js/4.4.1: 587 | resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 588 | dependencies: 589 | punycode: 2.1.1 590 | dev: true 591 | 592 | /v8-compile-cache/2.3.0: 593 | resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} 594 | dev: true 595 | 596 | /which/2.0.2: 597 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 598 | engines: {node: '>= 8'} 599 | hasBin: true 600 | dependencies: 601 | isexe: 2.0.0 602 | dev: true 603 | 604 | /word-wrap/1.2.3: 605 | resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} 606 | engines: {node: '>=0.10.0'} 607 | dev: true 608 | 609 | /wrappy/1.0.2: 610 | resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} 611 | dev: true 612 | 613 | /yallist/4.0.0: 614 | resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} 615 | dev: true 616 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Cynthia K. Rey, All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. Neither the name of the copyright holder nor the names of its contributors 13 | * may be used to endorse or promote products derived from this software without 14 | * specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | const { resolve } = require('path') 29 | const libgitFactory = require('./wasm/libgit.js') 30 | 31 | let libgitPromise 32 | async function getLibgit () { 33 | // Lazily init the wasm binary when we need it 34 | if (!libgitPromise) libgitPromise = libgitFactory() 35 | return libgitPromise 36 | } 37 | 38 | async function clone (repo, path) { 39 | const libgit = await getLibgit() 40 | path = await libgit.mount(resolve(path)) 41 | try { 42 | await libgit.clone(repo, path) 43 | } finally { 44 | libgit.umount(path) 45 | } 46 | } 47 | 48 | async function pull (path, skipFetch = false, force = false) { 49 | const libgit = await getLibgit() 50 | path = await libgit.mount(resolve(path)) 51 | try { 52 | await libgit.pull(path, skipFetch, force) 53 | } finally { 54 | libgit.umount(path) 55 | } 56 | } 57 | 58 | async function listUpdates (path) { 59 | const libgit = await getLibgit() 60 | path = await libgit.mount(resolve(path)) 61 | let res 62 | try { 63 | res = await libgit.listUpdates(path) 64 | } finally { 65 | libgit.umount(path) 66 | } 67 | 68 | return res; 69 | } 70 | 71 | const GIT_URL_RE = /^(?:https?:\/\/[^/]+\/|[^@]+@[^:]+:)(.*?)(?:\.git)?$/ 72 | async function readRepositoryMeta (path) { 73 | const libgit = await getLibgit() 74 | path = await libgit.mount(resolve(path)) 75 | let res 76 | try { 77 | res = await libgit.readRepositoryMeta(path) 78 | } finally { 79 | libgit.umount(path) 80 | } 81 | 82 | return { 83 | detached: Boolean(res[0]), 84 | branch: res[1] || null, 85 | revision: res[2] || null, 86 | upstream: res[3] || null, 87 | repo: res[3] ? res[3].match(GIT_URL_RE)?.[1] : null 88 | } 89 | } 90 | 91 | module.exports = { 92 | clone: clone, 93 | pull: pull, 94 | listUpdates: listUpdates, 95 | readRepositoryMeta: readRepositoryMeta 96 | } 97 | -------------------------------------------------------------------------------- /src/interface/alloc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Cynthia K. Rey, All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. Neither the name of the copyright holder nor the names of its contributors 13 | * may be used to endorse or promote products derived from this software without 14 | * specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | // eslint-disable-next-line no-unused-vars 29 | function allocString (str) { 30 | const length = lengthBytesUTF8(str) + 1 31 | const ptr = _malloc(length) 32 | stringToUTF8(str, ptr, length) 33 | return ptr 34 | } 35 | 36 | let deferredId = 0 37 | const deferredMap = new Map() 38 | // eslint-disable-next-line no-unused-vars 39 | function allocDeferred () { 40 | let resolve 41 | const ptr = deferredId++ 42 | const promise = new Promise((r) => (resolve = r)) 43 | deferredMap.set(ptr, resolve) 44 | return [ promise, ptr ] 45 | } 46 | 47 | // eslint-disable-next-line no-unused-vars 48 | function invokeDeferred (ptr, ...args) { 49 | const resolve = deferredMap.get(ptr) 50 | deferredMap.delete(ptr) 51 | resolve(...args) 52 | } 53 | 54 | // eslint-disable-next-line no-unused-vars 55 | function freeDeferred (ptr) { 56 | return deferredMap.delete(ptr) 57 | } 58 | 59 | let arrayId = 0 60 | const arrayMap = new Map() 61 | // eslint-disable-next-line no-unused-vars 62 | function allocArray () { 63 | const array = [] 64 | const ptr = arrayId++ 65 | arrayMap.set(ptr, array) 66 | return [ array, ptr ] 67 | } 68 | 69 | // eslint-disable-next-line no-unused-vars 70 | function arrayPush (ptr, ...data) { 71 | arrayMap.get(ptr).push(...data) 72 | } 73 | 74 | // eslint-disable-next-line no-unused-vars 75 | function arrayDeref (ptr) { 76 | return arrayMap.get(ptr); 77 | } 78 | 79 | // eslint-disable-next-line no-unused-vars 80 | function freeArray (ptr) { 81 | return arrayMap.delete(ptr) 82 | } 83 | -------------------------------------------------------------------------------- /src/interface/exports.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Cynthia K. Rey, All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. Neither the name of the copyright holder nor the names of its contributors 13 | * may be used to endorse or promote products derived from this software without 14 | * specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | const nodeFs = require('fs') 29 | 30 | /* eslint-disable dot-notation */ 31 | const existsSync = nodeFs['existsSync'] 32 | const mkdir = nodeFs['promises']['mkdir'] 33 | /* eslint-enable dot-notation */ 34 | 35 | async function mount (path) { 36 | if (!existsSync(path)) await mkdir(path) 37 | 38 | const dir = Math.random().toString(36).slice(2) 39 | FS.mkdir(dir) 40 | FS.mount(NODEFS, { root: path }, dir) 41 | return dir 42 | } 43 | 44 | function umount (path) { 45 | FS.unmount(path) 46 | FS.rmdir(path) 47 | } 48 | 49 | function free (allocated) { 50 | for (const a of allocated) { 51 | if (a[1]) { 52 | freeArray(a[0]) 53 | } else { 54 | _free(a[0]) 55 | } 56 | } 57 | } 58 | 59 | function makeWrapper (method, returns = false, threaded = true) { 60 | return async function (...rawArgs) { 61 | if (threaded) { 62 | // Wait for an available worker 63 | while (!PThread.unusedWorkers.length) { 64 | await new Promise((resolve) => setImmediate(resolve)) 65 | } 66 | } 67 | 68 | let ret = void 0 69 | const args = [] 70 | const toFree = [] 71 | const [ promise, deferredPtr ] = threaded ? allocDeferred() : [] 72 | for (const arg of rawArgs) { 73 | if (typeof arg === 'string') { 74 | const ptr = allocString(arg) 75 | toFree.push([ ptr ]) 76 | args.push(ptr) 77 | continue 78 | } 79 | 80 | args.push(arg) 81 | } 82 | 83 | if (returns) { 84 | const [ a, ptr ] = allocArray() 85 | args.push(ptr) 86 | ret = a 87 | } 88 | 89 | let res = Module[`_${method}`](...args, deferredPtr) 90 | if (threaded) { 91 | if (res < 0) { 92 | free(toFree) 93 | throw new Error('Failed to initialize git thread') 94 | } 95 | 96 | res = await promise 97 | } 98 | 99 | free(toFree) 100 | if (res < 0) { 101 | const error = new Error(`simple-git-wasm: call to ${method} failed: error code ${res}`) 102 | error.code = res 103 | throw error 104 | } 105 | 106 | return ret 107 | } 108 | } 109 | 110 | /* eslint-disable dot-notation */ 111 | Module['mount'] = mount 112 | Module['umount'] = umount 113 | Module['clone'] = makeWrapper('clone') 114 | Module['pull'] = makeWrapper('pull') 115 | Module['listUpdates'] = makeWrapper('list_updates', true) 116 | Module['readRepositoryMeta'] = makeWrapper('read_repository_meta', true, false) 117 | -------------------------------------------------------------------------------- /src/interface/http.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Cynthia K. Rey, All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. Neither the name of the copyright holder nor the names of its contributors 13 | * may be used to endorse or promote products derived from this software without 14 | * specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | const nodeHttp = require('http') 29 | const nodeHttps = require('https') 30 | 31 | let connectionId = 0 32 | const connections = new Map() 33 | 34 | // eslint-disable-next-line no-unused-vars 35 | function createConnection (url, isPost) { 36 | const connId = connectionId++ 37 | const client = url.startsWith('https') ? nodeHttps : nodeHttp 38 | const headers = {} 39 | let method = 'GET' 40 | 41 | if (isPost) { 42 | method = 'POST' 43 | headers['content-type'] = url.indexOf('git-upload-pack') > 0 44 | ? 'application/x-git-upload-pack-request' 45 | : 'application/x-git-receive-pack-request' 46 | } 47 | 48 | const req = client.request(url, { method: method, headers: headers }) 49 | connections.set(connId, { req: req, res: null }) 50 | return connId 51 | } 52 | 53 | // eslint-disable-next-line no-unused-vars 54 | function writeToConnection (connId, bufferPtr, length) { 55 | const conn = connections.get(connId) 56 | if (!conn) return -1 57 | 58 | // eslint-disable-next-line new-cap 59 | const buffer = GROWABLE_HEAP_U8().slice(bufferPtr, bufferPtr + length) 60 | conn.req.write(buffer) 61 | return 0 62 | } 63 | 64 | // eslint-disable-next-line no-unused-vars 65 | async function readFromConnection (connId, bufferPtr, length, outPtr) { 66 | const conn = connections.get(connId) 67 | if (!conn) return -1 68 | 69 | // [Cynthia] We don't have to worry about concurrent calls here, as this is 70 | // called by C code which is synchronous and paused until this call completes. 71 | if (!conn.res) { 72 | conn.req.end() 73 | conn.req.on('response', (res) => (conn.res = res)) 74 | } 75 | 76 | let buf 77 | // Emscripten (or the tooling it uses, whatever) doesn't support optional chaining 78 | while (!(buf = conn.res && conn.res.read(length))) await new Promise((resolve) => setImmediate(resolve)) 79 | writeArrayToMemory(buf, bufferPtr) 80 | 81 | // eslint-disable-next-line new-cap 82 | GROWABLE_HEAP_I32()[outPtr >> 2] = buf.length 83 | } 84 | 85 | // eslint-disable-next-line no-unused-vars 86 | function freeConnection (connId) { 87 | const conn = connections.get(connId) 88 | if (conn) { 89 | connections.delete(connId) 90 | // Silence any error that may arise from the destroy call 91 | conn.req.on('error', () => void 0) 92 | conn.req.destroy() 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/lib/http.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Cynthia K. Rey, All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. Neither the name of the copyright holder nor the names of its contributors 13 | * may be used to endorse or promote products derived from this software without 14 | * specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | // Replacement for libgit2 http transport, that uses js bindings to node' http api 29 | 30 | #include 31 | #include 32 | #include "smart.h" 33 | #include "http.h" 34 | 35 | // Constants -- copied from http.c and winhttp.c 36 | bool git_http__expect_continue = false; 37 | static const char* upload_pack_ls_service_url = "/info/refs?service=git-upload-pack"; 38 | static const char* upload_pack_service_url = "/git-upload-pack"; 39 | static const char* receive_pack_ls_service_url = "/info/refs?service=git-receive-pack"; 40 | static const char* receive_pack_service_url = "/git-receive-pack"; 41 | 42 | // Structs 43 | typedef struct { git_smart_subtransport parent; transport_smart* owner; bool fake_ssh; } emhttp_subtransport; 44 | typedef struct { git_smart_subtransport_stream parent; const char* service_url; int connection_id; } emhttp_stream; 45 | 46 | // HTTP interface 47 | static int emhttp_connection_read(int connId, const char* buf, size_t len) { 48 | int bytes_read = -2; 49 | MAIN_THREAD_ASYNC_EM_ASM({ readFromConnection($0, $1, $2, $3) }, connId, buf, len, &bytes_read); 50 | while (bytes_read == -2) { usleep(10); } 51 | return bytes_read; 52 | } 53 | 54 | static int emhttp_connection_alloc(char* url, bool is_post) { 55 | return MAIN_THREAD_EM_ASM_INT({ return createConnection(UTF8ToString($0), $1); }, url, is_post); 56 | } 57 | 58 | static int _emhttp_stream_read(git_smart_subtransport_stream* stream, char* buffer, size_t buf_size, size_t* bytes_read) { 59 | emhttp_stream* s = (emhttp_stream*) stream; 60 | if (s->connection_id == -1) { 61 | s->connection_id = emhttp_connection_alloc(s->service_url, false); 62 | } 63 | 64 | int read = emhttp_connection_read(s->connection_id, buffer, buf_size); 65 | if (read < 0) return read; 66 | *bytes_read = read; 67 | return 0; 68 | } 69 | 70 | static int _emhttp_stream_write(git_smart_subtransport_stream* stream, const char* buffer, size_t len) { 71 | emhttp_stream* s = (emhttp_stream*) stream; 72 | if (s->connection_id == -1) { 73 | s->connection_id = emhttp_connection_alloc(s->service_url, true); 74 | } 75 | 76 | return MAIN_THREAD_EM_ASM_INT({ return writeToConnection($0, $1, $2); }, s->connection_id, buffer, len); 77 | } 78 | 79 | static void _emhttp_stream_free(git_smart_subtransport_stream* stream) { 80 | emhttp_stream* s = (emhttp_stream*) stream; 81 | MAIN_THREAD_EM_ASM({ freeConnection($0); }, s->connection_id); 82 | git__free(s); 83 | } 84 | 85 | static int emhttp_stream_alloc(emhttp_subtransport* t, emhttp_stream** stream) { 86 | emhttp_stream* s; 87 | if (!stream) return -1; 88 | 89 | s = git__calloc(1, sizeof(emhttp_stream)); 90 | GIT_ERROR_CHECK_ALLOC(s); 91 | 92 | s->parent.subtransport = &t->parent; 93 | s->parent.read = _emhttp_stream_read; 94 | s->parent.write = _emhttp_stream_write; 95 | s->parent.free = _emhttp_stream_free; 96 | s->connection_id = -1; 97 | *stream = s; 98 | return 0; 99 | } 100 | 101 | // Git transport 102 | static int _emhttp_action(git_smart_subtransport_stream** stream, git_smart_subtransport* subtransport, char* url, git_smart_service_t action) { 103 | emhttp_subtransport* t = (emhttp_subtransport*) subtransport; 104 | emhttp_stream* s; 105 | 106 | if (emhttp_stream_alloc(t, &s) < 0) return -1; 107 | if (t->fake_ssh) { 108 | int c = 0; 109 | int len = 0; 110 | int pre = 0; 111 | while (url[c] != '\0') { 112 | if (len == 0) pre++; 113 | if (len != 0 || (len == 0 && url[c] == '@')) len++; 114 | c++; 115 | } 116 | 117 | const char* old_url = url; 118 | url = malloc((len + 8) * sizeof(char)); 119 | url[0] = 'h'; url[1] = 't'; url[2] = 't'; url[3] = 'p'; url[4] = 's'; url[5] = ':'; url[6] = '/'; url[7] = '/'; 120 | for (int i = 0; i < len; i++) url[i + 8] = old_url[i + pre] == ':' ? '/' : old_url[i + pre]; 121 | url[(len + 8)] = '\0'; 122 | } 123 | 124 | git_buf buf = GIT_BUF_INIT; 125 | if (action == GIT_SERVICE_UPLOADPACK_LS) { 126 | git_buf_printf(&buf, "%s%s", url, upload_pack_ls_service_url); 127 | } else if (action == GIT_SERVICE_UPLOADPACK) { 128 | git_buf_printf(&buf, "%s%s", url, upload_pack_service_url); 129 | } else if (action == GIT_SERVICE_RECEIVEPACK_LS) { 130 | git_buf_printf(&buf, "%s%s", url, receive_pack_ls_service_url); 131 | } else if (action == GIT_SERVICE_RECEIVEPACK) { 132 | git_buf_printf(&buf, "%s%s", url, receive_pack_service_url); 133 | } else { 134 | if (t->fake_ssh) free(url); 135 | return -1; 136 | } 137 | 138 | s->service_url = git_buf_cstr(&buf); 139 | *stream = &s->parent; 140 | if (t->fake_ssh) free(url); 141 | return 0; 142 | } 143 | 144 | static int _emhttp_close(git_smart_subtransport* subtransport) { 145 | // Nothing to do 146 | return 0; 147 | } 148 | 149 | static void _emhttp_free(git_smart_subtransport* subtransport) { 150 | emhttp_subtransport* t = (emhttp_subtransport*) subtransport; 151 | git__free(t); 152 | } 153 | 154 | int git_smart_subtransport_http(git_smart_subtransport** out, git_transport* owner, void* param) { 155 | emhttp_subtransport* t; 156 | if (!out) return -1; 157 | 158 | t = git__calloc(1, sizeof(emhttp_subtransport)); 159 | GIT_ERROR_CHECK_ALLOC(t); 160 | 161 | t->owner = (transport_smart*) owner; 162 | t->parent.action = _emhttp_action; 163 | t->parent.close = _emhttp_close; 164 | t->parent.free = _emhttp_free; 165 | t->fake_ssh = *((int*) param) == 554; 166 | *out = (git_smart_subtransport*) t; 167 | return 0; 168 | } 169 | -------------------------------------------------------------------------------- /src/lib/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Cynthia K. Rey, All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. Neither the name of the copyright holder nor the names of its contributors 13 | * may be used to endorse or promote products derived from this software without 14 | * specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | // libgit2 doesn't support shallow clone 34 | // see https://github.com/libgit2/libgit2/issues/3058 35 | 36 | #define UNUSED(X) (void)(X) 37 | 38 | // JS functions 39 | #define RESOLVE(PTR, RET) MAIN_THREAD_EM_ASM({ invokeDeferred($0, $1); }, PTR, RET) 40 | 41 | // Funni macros 42 | #define ERR_CHECK_RET(FN) ret = FN; if (ret < 0) return ret 43 | #define ERR_CHECK_LAB(FN, L) ret = FN; if (ret < 0) goto L 44 | #define ERR_CHECK_GET(A, B, C, ...) C 45 | #define ERR_CHECK(...) ERR_CHECK_GET(__VA_ARGS__, ERR_CHECK_LAB, ERR_CHECK_RET)(__VA_ARGS__) 46 | 47 | typedef struct { char* repository; char* path; int resolve_ptr; } clone_payload; 48 | typedef struct { char* path; int skip_fetch; int force; int resolve_ptr; } pull_payload; 49 | typedef struct { char* path; int ret_ptr; int resolve_ptr; } list_updates_payload; 50 | 51 | static int _process_submodule(git_submodule* submodule, const char* name, void* payload) { 52 | git_submodule_update_options* options = (git_submodule_update_options*) payload; 53 | if (options->version == 0) { 54 | git_submodule_update_options_init(options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION); 55 | options->fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; 56 | } 57 | 58 | return git_submodule_update(submodule, 1, options); 59 | } 60 | 61 | static int _extract_oid(const char* ref_name, const char* remote_url, const git_oid* oid, unsigned int is_merge, void* payload) { 62 | UNUSED(ref_name); 63 | UNUSED(remote_url); 64 | if (is_merge) { 65 | git_oid_cpy((git_oid*) payload, oid); 66 | return 1; 67 | } 68 | return 0; 69 | } 70 | 71 | static int update_submodules(git_repository* repo) { 72 | int ret = 0; 73 | git_submodule_update_options submodule_options; 74 | ret = git_submodule_foreach(repo, _process_submodule, &submodule_options); 75 | return ret; 76 | } 77 | 78 | static int stash_changes(git_repository* repo) { 79 | // Code from this function pretty much taken from libgit2's example stash.c. 80 | int ret = 0; 81 | git_signature* signature; 82 | git_oid stashid; 83 | 84 | ERR_CHECK(git_signature_now(&signature, "simple-git-wasm", "none@example.com")); 85 | 86 | // Get message 87 | time_t t = time(NULL); 88 | struct tm* tm = localtime(&t); 89 | char* message = malloc(64); 90 | strftime(message, 64, "[%d/%m/%y %H:%M:%S] simple-git-wasm: changes before pull", tm); 91 | 92 | // Stash changes 93 | ret = git_stash_save(&stashid, repo, signature, message, GIT_STASH_DEFAULT); 94 | git_signature_free(signature); 95 | free(message); 96 | return ret; 97 | } 98 | 99 | static void get_short_commit_message(const git_commit* commit, char* buf) { 100 | const char* msg = git_commit_message_raw(commit); 101 | int i; 102 | 103 | for (i = 0; i < 70; i++) { 104 | if (msg[i] == '\n') break; 105 | buf[i] = msg[i]; 106 | } 107 | 108 | if (i == 70 && msg[i] != '\n') { 109 | buf[i++] = (char) 226; 110 | buf[i++] = (char) 128; 111 | buf[i++] = (char) 166; 112 | } 113 | 114 | buf[i] = '\0'; 115 | } 116 | 117 | static void* clone_repository(void* payload) { 118 | int ret = 0; 119 | git_repository* repo; 120 | git_clone_options clone_options; 121 | clone_payload* opts = (clone_payload*) payload; 122 | 123 | git_clone_options_init(&clone_options, GIT_CLONE_OPTIONS_VERSION); 124 | clone_options.fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; 125 | 126 | ERR_CHECK(git_clone(&repo, opts->repository, opts->path, NULL), clone_end); 127 | ret = update_submodules(repo); 128 | clone_end: 129 | git_repository_free(repo); 130 | RESOLVE(opts->resolve_ptr, ret); 131 | free(payload); 132 | return NULL; 133 | } 134 | 135 | static void* pull_repository(void* payload) { 136 | int ret = 0; 137 | git_repository* repo; 138 | git_remote* remote; 139 | git_reference* ref_current; 140 | git_reference* ref_updated; 141 | git_object* target; 142 | 143 | git_oid oid; 144 | git_fetch_options fetch_options; 145 | git_checkout_options checkout_options; 146 | pull_payload* opts = (pull_payload*) payload; 147 | 148 | ERR_CHECK(git_repository_open(&repo, opts->path), pull_end); 149 | 150 | if (opts->force) { 151 | // [Cynthia] Design choice from Powercord here; force-pull = discard all local changes. 152 | // We used to reset --hard, here we stash the local changes so they aren't completely lost. 153 | ERR_CHECK(stash_changes(repo), pull_end); 154 | } 155 | 156 | // Lookup the remote 157 | ERR_CHECK(git_remote_lookup(&remote, repo, "origin"), pull_end); 158 | 159 | if (opts->skip_fetch == 0) { 160 | // Update FETCH_HEAD 161 | git_fetch_options_init(&fetch_options, GIT_FETCH_OPTIONS_VERSION); 162 | ERR_CHECK(git_remote_fetch(remote, NULL, &fetch_options, NULL), pull_end); 163 | } 164 | 165 | // Find out the object id to merge 166 | ERR_CHECK(git_repository_fetchhead_foreach(repo, _extract_oid, &oid), pull_end); 167 | 168 | // Retrieve current reference 169 | ERR_CHECK(git_repository_head(&ref_current, repo), pull_end); 170 | 171 | // Lookup target object 172 | ERR_CHECK(git_object_lookup(&target, repo, &oid, GIT_OBJECT_COMMIT), pull_end); 173 | 174 | // Fast-forward checkout 175 | git_checkout_options_init(&checkout_options, GIT_CHECKOUT_OPTIONS_VERSION); 176 | checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE; 177 | ERR_CHECK(git_checkout_tree(repo, target, &checkout_options), pull_end); 178 | 179 | // Move references 180 | ERR_CHECK(git_reference_set_target(&ref_updated, ref_current, &oid, NULL), pull_end); 181 | 182 | // Update submodules 183 | ret = update_submodules(repo); 184 | pull_end: 185 | // Cleanup & return 186 | git_reference_free(ref_current); 187 | git_reference_free(ref_updated); 188 | git_object_free(target); 189 | git_remote_free(remote); 190 | git_repository_free(repo); 191 | RESOLVE(opts->resolve_ptr, ret); 192 | free(payload); 193 | return NULL; 194 | } 195 | 196 | static void* list_repository_updates(void* payload) { 197 | int ret = 0; 198 | int entries; 199 | git_repository* repo; 200 | git_remote* remote; 201 | git_revwalk* walker; 202 | git_oid local_oid; 203 | git_oid remote_oid; 204 | 205 | git_fetch_options fetch_options; 206 | list_updates_payload* opts = (list_updates_payload*) payload; 207 | 208 | ERR_CHECK(git_repository_open(&repo, opts->path), list_end); 209 | 210 | // Lookup the remote 211 | ERR_CHECK(git_remote_lookup(&remote, repo, "origin"), list_end); 212 | 213 | // Update FETCH_HEAD 214 | git_fetch_options_init(&fetch_options, GIT_FETCH_OPTIONS_VERSION); 215 | ERR_CHECK(git_remote_fetch(remote, NULL, &fetch_options, NULL), list_end); 216 | 217 | // Lookup OIDs 218 | ERR_CHECK(git_reference_name_to_id(&local_oid, repo, "HEAD"), list_end); 219 | ERR_CHECK(git_repository_fetchhead_foreach(repo, _extract_oid, &remote_oid), list_end); 220 | 221 | // Setup walker 222 | ERR_CHECK(git_revwalk_new(&walker, repo), list_end); 223 | ERR_CHECK(git_revwalk_push(walker, &remote_oid), list_end); 224 | ERR_CHECK(git_revwalk_sorting(walker, GIT_SORT_TIME & GIT_SORT_REVERSE), list_end); 225 | 226 | // Walk through commits 227 | git_oid oid; 228 | git_commit* commit; 229 | while (git_revwalk_next(&oid, walker) == 0) { 230 | if (git_oid_cmp(&oid, &local_oid) == 0) break; 231 | 232 | ERR_CHECK(git_commit_lookup(&commit, repo, &oid), list_end); 233 | 234 | char* message = malloc(74); 235 | get_short_commit_message(commit, message); 236 | 237 | char* author = git_commit_author(commit)->name; 238 | int64_t date = git_commit_time(commit); 239 | 240 | // todo: list updates files? 241 | MAIN_THREAD_EM_ASM({ 242 | arrayPush($0, { 243 | ["id"]: UTF8ToString($1), 244 | ["message"]: UTF8ToString($2), 245 | ["author"]: UTF8ToString($3), 246 | ["date"]: new Date(($4 * 1e3) + (($5 << 32) * 1e3)) 247 | }); 248 | }, 249 | opts->ret_ptr, 250 | git_oid_tostr_s(&oid), 251 | message, 252 | author, 253 | // 64 bits values don't play nicely 254 | (int) (date & 0xffffffff), 255 | (int) (date >> 32) 256 | ); 257 | 258 | git_commit_free(commit); 259 | free(message); 260 | } 261 | 262 | list_end: 263 | // Cleanup & return 264 | git_revwalk_free(walker); 265 | git_remote_free(remote); 266 | git_repository_free(repo); 267 | RESOLVE(opts->resolve_ptr, ret); 268 | free(payload); 269 | return NULL; 270 | } 271 | 272 | EMSCRIPTEN_KEEPALIVE 273 | int clone(char* repository, char* path, int resolve_ptr) { 274 | int ret; 275 | pthread_t pid; 276 | clone_payload* payload; 277 | 278 | payload = malloc(sizeof(clone_payload)); 279 | payload->repository = repository; 280 | payload->path = path; 281 | payload->resolve_ptr = resolve_ptr; 282 | 283 | ERR_CHECK(pthread_create(&pid, NULL, clone_repository, (void*) payload)); 284 | ERR_CHECK(pthread_detach(pid)); 285 | return 0; 286 | } 287 | 288 | EMSCRIPTEN_KEEPALIVE 289 | int pull(char* path, int skip_fetch, int force, int resolve_ptr) { 290 | int ret; 291 | pthread_t pid; 292 | pull_payload* payload; 293 | 294 | payload = malloc(sizeof(pull_payload)); 295 | payload->path = path; 296 | payload->skip_fetch = skip_fetch; 297 | payload->force = force; 298 | payload->resolve_ptr = resolve_ptr; 299 | 300 | ERR_CHECK(pthread_create(&pid, NULL, pull_repository, (void*) payload)); 301 | ERR_CHECK(pthread_detach(pid)); 302 | return 0; 303 | } 304 | 305 | EMSCRIPTEN_KEEPALIVE 306 | int list_updates(char* path, int ret_ptr, int resolve_ptr) { 307 | int ret; 308 | pthread_t pid; 309 | list_updates_payload* payload; 310 | 311 | payload = malloc(sizeof(list_updates_payload)); 312 | payload->path = path; 313 | payload->ret_ptr = ret_ptr; 314 | payload->resolve_ptr = resolve_ptr; 315 | 316 | ERR_CHECK(pthread_create(&pid, NULL, list_repository_updates, (void*) payload)); 317 | ERR_CHECK(pthread_detach(pid)); 318 | return 0; 319 | } 320 | 321 | EMSCRIPTEN_KEEPALIVE 322 | int read_repository_meta(char* path, int ret_ptr) { 323 | int ret = 0; 324 | git_oid revision; 325 | 326 | git_repository* repo; 327 | git_reference* head_ref; 328 | git_remote* remote; 329 | 330 | ERR_CHECK(git_repository_open(&repo, path), cfg_end); 331 | 332 | ret = git_repository_head(&head_ref, repo); 333 | if (ret < 0 && ret != GIT_EUNBORNBRANCH && ret != GIT_ENOTFOUND) goto cfg_end; 334 | 335 | ret = git_reference_name_to_id(&revision, repo, "HEAD"); 336 | if (ret < 0 && ret != GIT_ENOTFOUND) goto cfg_end; 337 | 338 | ret = git_remote_lookup(&remote, repo, "origin"); 339 | if (ret < 0 && ret != GIT_ENOTFOUND) goto cfg_end; 340 | 341 | EM_ASM( 342 | { arrayPush($0, $1, UTF8ToString($2), UTF8ToString($3), UTF8ToString($4), UTF8ToString($6)) }, 343 | ret_ptr, 344 | git_repository_head_detached(repo), 345 | head_ref != NULL ? git_reference_shorthand(head_ref) : "", 346 | revision.id != NULL ? git_oid_tostr_s(&revision) : "", 347 | remote != NULL ? git_remote_url(remote) : "" 348 | ); 349 | 350 | cfg_end: 351 | git_remote_free(remote); 352 | git_reference_free(head_ref); 353 | git_repository_free(repo); 354 | return ret; 355 | } 356 | 357 | int main() { 358 | git_libgit2_init(); 359 | return 0; 360 | } 361 | -------------------------------------------------------------------------------- /src/lib/ssh.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Cynthia K. Rey, All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this 8 | * list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright notice, 10 | * this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. Neither the name of the copyright holder nor the names of its contributors 13 | * may be used to endorse or promote products derived from this software without 14 | * specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | // "Replacement" for libgit2 ssh transport, which just tries to transform the query to http 29 | 30 | #include 31 | #include "smart.h" 32 | #include "ssh.h" 33 | #include "http.h" 34 | 35 | const int fake_ssh = 554; 36 | 37 | int git_smart_subtransport_ssh(git_smart_subtransport** out, git_transport* owner, void* param) { 38 | return git_smart_subtransport_http(out, owner, (void*) &fake_ssh); 39 | } 40 | 41 | int git_transport_ssh_with_paths(git_transport** out, git_remote* owner, void* param) { 42 | return git_smart_subtransport_http(out, owner, (void*) &fake_ssh); 43 | } 44 | 45 | // Unused but required 46 | int git_transport_ssh_global_init(void) { return 0; } 47 | --------------------------------------------------------------------------------