diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 0934761..8bb5f93 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -20,13 +20,14 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v2 with: - go-version: ^1.14 + go-version: ^1.16 id: go - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Link workspace with GOPATH + continue-on-error: false run: | sudo mkdir -vp $(dirname ${GOPATH}/src/github.com/${GITHUB_REPOSITORY}) sudo chown -R ${USER}:users $(dirname ${GOPATH}/src/github.com/${GITHUB_REPOSITORY}) @@ -34,17 +35,19 @@ jobs: sudo ln -svf $(pwd) ${GOPATH}/src/github.com/${GITHUB_REPOSITORY} - name: Check out tools into the Go module directory + continue-on-error: false run: | git clone https://github.com/nabbar/gotools.git scripts ./scripts/prepare + - name: Update vendor or dependancies + continue-on-error: false + run: ./scripts/ci_depend + - name: Check goFmt & goImport continue-on-error: false run: ./scripts/ci_format - - name: Update vendor or dependancies - run: ./scripts/ci_depend - - name: Check Missing License continue-on-error: false run: ./scripts/ci_license diff --git a/.golangci.yml b/.golangci.yml index cd1fbc1..263e6c7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,23 +1,88 @@ -# This file contains all available configuration options -# with their default values. +linters-settings: + dupl: + threshold: 100 + funlen: + lines: 250 + statements: 200 + gci: + local-prefixes: github.com/golangci/golangci-lint + goconst: + min-len: 2 + min-occurrences: 2 + gocyclo: + min-complexity: 50 + goimports: + local-prefixes: github.com/golangci/golangci-lint + gomnd: + settings: + mnd: + # don't include the "operation" and "assign" + checks: argument,case,condition,return + govet: + check-shadowing: true + settings: + printf: + funcs: + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf + lll: + line-length: 140 + maligned: + suggest-new: true + misspell: + locale: US + +linters: + # please, do not use `enable-all`: it's deprecated and will be removed soon. + # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint + disable-all: true + enable: + - bodyclose + - deadcode + - dogsled + - dupl + - errcheck + - exportloopref + - exhaustive + - funlen + - goconst + - gocyclo + - gofmt + - goimports + - gomnd + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - lll + - misspell + - nakedret + - noctx + - rowserrcheck + - staticcheck + - structcheck + - stylecheck + - typecheck + - unconvert + - unparam + - unused + - varcheck -# options for analysis running run: # default concurrency is a available CPU number - concurrency: 4 - + #concurrency: 4 # timeout for analysis, e.g. 30s, 5m, default is 1m - timeout: 30m - + timeout: 10m # exit code when at least one issue was found, default is 1 - issues-exit-code: 0 - + issues-exit-code: 1 # include test files or not, default is true - tests: true - + tests: false # list of build tags, all linters use it. Default is empty list. - build-tags: [] - + #build-tags: + # - mytag # which dirs to skip: issues from them won't be reported; # can use regexp here: generated.*, regexp is applied on full path; # default value is empty list, but default dirs are skipped independently @@ -25,22 +90,21 @@ run: # "/" will be replaced by current OS file path separator to properly work # on Windows. skip-dirs: - - vendor - - test - + - test/ + - vendor/ + - .* # default is true. Enables skipping of directories: # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ - skip-dirs-use-default: true - + #skip-dirs-use-default: true # which files to skip: they will be analyzed, but issues from them # won't be reported. Default value is empty list, but there is # no need to include all autogenerated files, we confidently recognize # autogenerated files. If it's not please let us know. # "/" will be replaced by current OS file path separator to properly work # on Windows. - skip-files: - - ".*\\.my\\.go$" - + #skip-files: + # - ".*\\.my\\.go$" + # - lib/bad.go # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": # If invoked with -mod=readonly, the go command is disallowed from the implicit # automatic updating of go.mod described above. Instead, it fails when any changes @@ -49,401 +113,20 @@ run: # If invoked with -mod=vendor, the go command assumes that the vendor # directory holds the correct copies of dependencies and ignores # the dependency descriptions in go.mod. - #modules-download-mode: readonly|release|vendor - + modules-download-mode: vendor # Allow multiple parallel golangci-lint instances running. # If false (default) - golangci-lint acquires file lock on start. - allow-parallel-runners: false - + allow-parallel-runners: true # output configuration options output: # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" format: colored-line-number - # print lines of code with issue, default is true print-issued-lines: true - # print linter name in the end of issue text, default is true print-linter-name: true - # make issues output unique by line, default is true uniq-by-line: true - # add a prefix to the output file references; default is no prefix path-prefix: "" - - -# all available settings of specific linters -linters-settings: - dogsled: - # checks assignments with too many blank identifiers; default is 2 - max-blank-identifiers: 2 - dupl: - # tokens count to trigger issue, 150 by default - threshold: 100 - errcheck: - # report about not checking of errors in type assertions: `a := b.(MyStruct)`; - # default is false: such cases aren't reported by default. - check-type-assertions: false - - # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; - # default is false: such cases aren't reported by default. - check-blank: false - - # [deprecated] comma-separated list of pairs of the form pkg:regex - # the regex is used to ignore names within pkg. (default "fmt:.*"). - # see https://github.com/kisielk/errcheck#the-deprecated-method for details - ignore: fmt:.*,io/ioutil:^Read.* - - # path to a file containing a list of functions to exclude from checking - # see https://github.com/kisielk/errcheck#excluding-functions for details - # exclude: /path/to/file.txt - exhaustive: - # indicates that switch statements are to be considered exhaustive if a - # 'default' case is present, even if all enum members aren't listed in the - # switch - default-signifies-exhaustive: false - funlen: - lines: 1000 - statements: 100 - gocognit: - # minimal code complexity to report, 30 by default (but we recommend 10-20) - min-complexity: 50 - nestif: - # minimal complexity of if statements to report, 5 by default - min-complexity: 4 - goconst: - # minimal length of string constant, 3 by default - min-len: 3 - # minimal occurrences count to trigger, 3 by default - min-occurrences: 3 - gocritic: - # Which checks should be enabled; can't be combined with 'disabled-checks'; - # See https://go-critic.github.io/overview#checks-overview - # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` - # By default list of stable checks is used. - # enabled-checks: - # - rangeValCopy - - # Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty - disabled-checks: - - regexpMust - - # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks. - # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". - enabled-tags: - - performance - disabled-tags: - - experimental - - settings: # settings passed to gocritic - captLocal: # must be valid enabled check name - paramsOnly: true - rangeValCopy: - sizeThreshold: 32 - gocyclo: - # minimal code complexity to report, 30 by default (but we recommend 10-20) - min-complexity: 50 - godot: - # check all top-level comments, not only declarations - check-all: false - godox: - # report any comments starting with keywords, this is useful for TODO or FIXME comments that - # might be left in the code accidentally and should be resolved before merging - keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting - - NOTE - - OPTIMIZE # marks code that should be optimized before merging - - HACK # marks hack-arounds that should be removed before merging - gofmt: - # simplify code: gofmt with `-s` option, true by default - simplify: true - goheader: - values: - const: - # define here const type values in format k:v, for example: - # YEAR: 2020 - # COMPANY: MY COMPANY - regexp: - # define here regexp type values, for example - # AUTHOR: .*@mycompany\.com - template: - # put here copyright header template for source code files, for example: - # {{ AUTHOR }} {{ COMPANY }} {{ YEAR }} - # SPDX-License-Identifier: Apache-2.0 - # - # Licensed under the Apache License, Version 2.0 (the "License"); - # you may not use this file except in compliance with the License. - # You may obtain a copy of the License at: - # - # http://www.apache.org/licenses/LICENSE-2.0 - # - # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, - # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - # See the License for the specific language governing permissions and - # limitations under the License. - template-path: - # also as alternative of directive 'template' you may put the path to file with the template source - goimports: - # put imports beginning with prefix after 3rd-party packages; - # it's a comma-separated list of prefixes - local-prefixes: github.com/org/project - golint: - # minimal confidence for issues, default is 0.8 - min-confidence: 0.8 - gomnd: - settings: - mnd: - # the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description. - checks: argument,case,condition,operation,return,assign - gomodguard: - allowed: - modules: # List of allowed modules - # - gopkg.in/yaml.v2 - domains: # List of allowed module domains - # - golang.org - blocked: - modules: # List of blocked modules - # - github.com/uudashr/go-module: # Blocked module - # recommendations: # Recommended modules that should be used instead (Optional) - # - golang.org/x/mod - # reason: "`mod` is the official go.mod parser library." # Reason why the recommended module should be used (Optional) - versions: # List of blocked module version constraints - # - github.com/mitchellh/go-homedir: # Blocked module with version constraint - # version: "< 1.1.0" # Version constraint, see https://github.com/Masterminds/semver#basic-comparisons - # reason: "testing if blocked version constraint works." # Reason why the version constraint exists. (Optional) - govet: - # report about shadowed variables - check-shadowing: true - - # settings per analyzer - settings: - printf: # analyzer name, run `go tool vet help` to see all analyzers - funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer - - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof - - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf - - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf - - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf - - # enable or disable analyzers by name - enable: - - atomicalign - enable-all: false - disable: - - shadow - disable-all: false - depguard: - list-type: blacklist - include-go-root: false - packages: - # - github.com/sirupsen/logrus - packages-with-error-message: - # specify an error message to output when a blacklisted package is used - # - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" - lll: - # max line length, lines longer will be reported. Default is 120. - # '\t' is counted as 1 character by default, and can be changed with the tab-width option - line-length: 240 - # tab width in spaces. Default to 1. - tab-width: 1 - maligned: - # print struct with more effective memory layout or not, false by default - suggest-new: true - misspell: - # Correct spellings using locale preferences for US or UK. - # Default is to use a neutral variety of English. - # Setting locale to US will correct the British spelling of 'colour' to 'color'. - locale: US - ignore-words: - - someword - nakedret: - # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 - max-func-lines: 30 - prealloc: - # XXX: we don't recommend using this linter before doing performance profiling. - # For most programs usage of prealloc will be a premature optimization. - - # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. - # True by default. - simple: true - range-loops: true # Report preallocation suggestions on range loops, true by default - for-loops: false # Report preallocation suggestions on for loops, false by default - nolintlint: - # Enable to ensure that nolint directives are all used. Default is true. - allow-unused: false - # Disable to ensure that nolint directives don't have a leading space. Default is true. - allow-leading-space: true - # Exclude following linters from requiring an explanation. Default is []. - allow-no-explanation: [] - # Enable to require an explanation of nonzero length after each nolint directive. Default is false. - require-explanation: true - # Enable to require nolint directives to mention the specific linter being suppressed. Default is false. - require-specific: true - rowserrcheck: - packages: - - github.com/jmoiron/sqlx - testpackage: - # regexp pattern to skip files - skip-regexp: (export|internal)_test\.go - unparam: - # Inspect exported functions, default is false. Set to true if no external program/library imports your code. - # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: - # if it's called for subdir of a project it can't find external interfaces. All text editor integrations - # with golangci-lint call it on a directory with the changed file. - check-exported: false - unused: - # treat code as a program (not a library) and report unused exported identifiers; default is false. - # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: - # if it's called for subdir of a project it can't find funcs usages. All text editor integrations - # with golangci-lint call it on a directory with the changed file. - check-exported: false - whitespace: - multi-if: false # Enforces newlines (or comments) after every multi-line if statement - multi-func: false # Enforces newlines (or comments) after every multi-line function signature - wsl: - # If true append is only allowed to be cuddled if appending value is - # matching variables, fields or types on line above. Default is true. - strict-append: true - # Allow calls and assignments to be cuddled as long as the lines have any - # matching variables, fields or types. Default is true. - allow-assign-and-call: true - # Allow multiline assignments to be cuddled. Default is true. - allow-multiline-assign: true - # Allow declarations (var) to be cuddled. - allow-cuddle-declarations: true - # Allow trailing comments in ending of blocks - allow-trailing-comment: false - # Force newlines in end of case at this limit (0 = never). - force-case-trailing-whitespace: 0 - # Force cuddling of err checks with err var assignment - force-err-cuddling: false - # Allow leading comments to be separated with empty liens - allow-separated-leading-comment: false - gofumpt: - # Choose whether or not to use the extra rules that are disabled - # by default - extra-rules: false - - # The custom section can be used to define linter plugins to be loaded at runtime. See README doc - # for more info. - #custom: - # Each custom linter should have a unique name. - #example: - # The path to the plugin *.so. Can be absolute or local. Required for each custom linter - #path: /path/to/example.so - # The description of the linter. Optional, just for documentation purposes. - #description: This is an example usage of a plugin linter. - # Intended to point to the repo location of the linter. Optional, just for documentation purposes. - #original-url: github.com/golangci/example-linter - -linters: - fast: false - enable-all: true - disable: - - gochecknoinits - - gochecknoglobals - - wsl - - golint - - gocritic - - stylecheck - - whitespace - - gofumpt - - nestif - - nolintlint - - nlreturn - -issues: - # List of regexps of issue texts to exclude, empty list by default. - # But independently from this option we use default exclude patterns, - # it can be disabled by `exclude-use-default: false`. To list all - # excluded by default patterns execute `golangci-lint run --help` - exclude: - - .*test.*\.go - - - # Excluding configuration per-path, per-linter, per-text and per-source - exclude-rules: - # Exclude some linters from running on tests files. - - path: _test\.go - linters: - - gocyclo - - errcheck - - dupl - - gosec - - # Exclude known linters from partially hard-vendored code, - # which is impossible to exclude via "nolint" comments. - - path: internal/hmac/ - text: "weak cryptographic primitive" - linters: - - gosec - - # Exclude some staticcheck messages - - linters: - - staticcheck - text: "SA9003:" - - # Exclude lll issues for long lines with go:generate - - linters: - - lll - source: "^//go:generate " - - # Independently from option `exclude` we use default exclude patterns, - # it can be disabled by this option. To list all - # excluded by default patterns execute `golangci-lint run --help`. - # Default value for this option is true. - exclude-use-default: false - - # The default value is false. If set to true exclude and exclude-rules - # regular expressions become case sensitive. - exclude-case-sensitive: false - - # The list of ids of default excludes to include or disable. By default it's empty. - include: - - EXC0002 # disable excluding of issues about comments from golint - - # Maximum issues count per one linter. Set to 0 to disable. Default is 50. - max-issues-per-linter: 0 - - # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. - max-same-issues: 0 - - # Show only new issues: if there are unstaged changes or untracked files, - # only those changes are analyzed, else only changes in HEAD~ are analyzed. - # It's a super-useful option for integration of golangci-lint into existing - # large codebase. It's not practical to fix all existing issues at the moment - # of integration: much better don't allow issues in new code. - # Default is false. - new: false - - # Show only new issues created after git revision `REV` - #new-from-rev: REV - - # Show only new issues created in git patch with set file path. - #new-from-patch: path/to/patch/file - -severity: - # Default value is empty string. - # Set the default severity for issues. If severity rules are defined and the issues - # do not match or no severity is provided to the rule this will be the default - # severity applied. Severities should match the supported severity names of the - # selected out format. - # - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity - # - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity - # - Github: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message - default-severity: error - - # The default value is false. - # If set to true severity-rules regular expressions become case sensitive. - case-sensitive: false - - # Default value is empty list. - # When a list of severity rules are provided, severity information will be added to lint - # issues. Severity rules have the same filtering capability as exclude rules except you - # are allowed to specify one matcher per severity rule. - # Only affects out formats that support setting severity information. - rules: - - linters: - - dupl - severity: info diff --git a/archive/archive.go b/archive/archive.go index b95455c..a17b1b4 100644 --- a/archive/archive.go +++ b/archive/archive.go @@ -43,11 +43,9 @@ import ( type ArchiveType uint8 const ( - ArchiveTypeUnknown = iota - ArchiveTypeZip - ArchiveTypeTar - ArchiveTypeTarGzip - ArchiveTypeGzip + TypeTar = iota + 1 + TypeTarGzip + TypeGzip ) func ExtractFile(src, dst libiot.FileProgress, fileNameContain, fileNameRegex string) liberr.Error { @@ -204,15 +202,16 @@ func ExtractAll(src libiot.FileProgress, originalName, outputPath string, defaul func CreateArchive(archiveType ArchiveType, archive libiot.FileProgress, pathContent ...string) (created bool, err liberr.Error) { //@TODO: make function if len(pathContent) < 1 { + //nolint #goerr113 return false, ErrorParamsEmpty.ErrorParent(fmt.Errorf("pathContent is empty")) } switch archiveType { - case ArchiveTypeGzip: + case TypeGzip: return libgzp.Create(archive, pathContent...) - case ArchiveTypeTar: + case TypeTar: return libtar.Create(archive, pathContent...) - case ArchiveTypeTarGzip: + case TypeTarGzip: return libtar.CreateGzip(archive, pathContent...) } diff --git a/archive/gzip/writer.go b/archive/gzip/writer.go index ff47e61..62e7fab 100644 --- a/archive/gzip/writer.go +++ b/archive/gzip/writer.go @@ -34,10 +34,9 @@ import ( "os" liberr "github.com/nabbar/golib/errors" - libiot "github.com/nabbar/golib/ioutils" ) -func Create(archive libiot.FileProgress, content ...string) (bool, liberr.Error) { +func Create(archive io.WriteSeeker, content ...string) (bool, liberr.Error) { var ( w *gzip.Writer f *os.File @@ -46,6 +45,7 @@ func Create(archive libiot.FileProgress, content ...string) (bool, liberr.Error) ) if len(content) != 1 { + //nolint #goerr113 return false, ErrorParamsMismatching.ErrorParent(fmt.Errorf("content path must be limited to strictly one contents")) } diff --git a/archive/tar/writer.go b/archive/tar/writer.go index 5195574..702abd4 100644 --- a/archive/tar/writer.go +++ b/archive/tar/writer.go @@ -36,185 +36,125 @@ import ( "path/filepath" liberr "github.com/nabbar/golib/errors" - libiot "github.com/nabbar/golib/ioutils" ) -func Create(archive libiot.FileProgress, content ...string) (bool, liberr.Error) { +func Create(archive io.WriteSeeker, content ...string) (bool, liberr.Error) { - var ( - t *tar.Writer - n int64 - - err error - lEr = ErrorTarCreateAddFile.Error(nil) - ) - - if _, err = archive.Seek(0, io.SeekStart); err != nil { + if _, err := archive.Seek(0, io.SeekStart); err != nil { return false, ErrorFileSeek.ErrorParent(err) } - t = tar.NewWriter(archive) - - for i := 0; i < len(content); i++ { - if content[i] == "" { - continue - } - - err = filepath.Walk(content[i], func(file string, inf os.FileInfo, err error) error { - var ( - e error - h *tar.Header - f *os.File - ) - - // generate tar header - h, e = tar.FileInfoHeader(inf, file) - if e != nil { - return e - } - - // must provide real name - // (see https://golang.org/src/archive/tar/common.go?#L626) - h.Name = filepath.ToSlash(file) - - // write header - if e = t.WriteHeader(h); err != nil { - return err - } - - // if not a dir, write file content - if !inf.IsDir() { - f, e = os.Open(file) - - if e != nil { - return e - } - - if _, e = io.Copy(t, f); e != nil { - return e - } - } - - n++ - return nil - }) - - if err != nil { - lEr.AddParent(err) - continue - } + if ok, err := createTar(archive, content...); err != nil || !ok { + return ok, err } - if n < 1 { - if lEr.HasParent() { - return false, lEr - } - - return false, ErrorTarCreate.ErrorParent(fmt.Errorf("no file to add in archive")) - } else if !lEr.HasParent() { - lEr = nil - } - - if err = t.Close(); err != nil { - return false, ErrorTarCreate.ErrorParent(err) - } - - if _, err = archive.Seek(0, io.SeekStart); err != nil { + if _, err := archive.Seek(0, io.SeekStart); err != nil { return false, ErrorFileSeek.ErrorParent(err) } - return true, lEr + return true, nil } -func CreateGzip(archive libiot.FileProgress, content ...string) (bool, liberr.Error) { +func CreateGzip(archive io.WriteSeeker, content ...string) (bool, liberr.Error) { - var ( - z *gzip.Writer - t *tar.Writer - n int64 - - err error - lEr = ErrorTarCreateAddFile.Error(nil) - ) - - if _, err = archive.Seek(0, io.SeekStart); err != nil { + if _, err := archive.Seek(0, io.SeekStart); err != nil { return false, ErrorFileSeek.ErrorParent(err) } - z = gzip.NewWriter(archive) - t = tar.NewWriter(z) + z := gzip.NewWriter(archive) - for i := 0; i < len(content); i++ { - if content[i] == "" { - continue - } - - err = filepath.Walk(content[i], func(file string, inf os.FileInfo, err error) error { - var ( - e error - h *tar.Header - f *os.File - ) - - // generate tar header - h, e = tar.FileInfoHeader(inf, file) - if e != nil { - return e - } - - // must provide real name - // (see https://golang.org/src/archive/tar/common.go?#L626) - h.Name = filepath.ToSlash(file) - - // write header - if e = t.WriteHeader(h); err != nil { - return err - } - - // if not a dir, write file content - if !inf.IsDir() { - f, e = os.Open(file) - - if e != nil { - return e - } - - if _, e = io.Copy(t, f); e != nil { - return e - } - } - - n++ - return nil - }) - - if err != nil { - lEr.AddParent(err) - continue - } + if ok, err := createTar(z, content...); err != nil || !ok { + return ok, err } - if n < 1 { - if lEr.HasParent() { - return false, lEr - } - - return false, ErrorTarCreate.ErrorParent(fmt.Errorf("no file to add in archive")) - } else if !lEr.HasParent() { - lEr = nil - } - - if err = t.Close(); err != nil { - return false, ErrorTarCreate.ErrorParent(err) - } - - if err = z.Close(); err != nil { + if err := z.Close(); err != nil { return false, ErrorGzipCreate.ErrorParent(err) } - if _, err = archive.Seek(0, io.SeekStart); err != nil { + if _, err := archive.Seek(0, io.SeekStart); err != nil { return false, ErrorFileSeek.ErrorParent(err) } + return true, nil +} + +func createTar(w io.Writer, content ...string) (bool, liberr.Error) { + var ( + t *tar.Writer + n int64 + + err error + lEr = ErrorTarCreateAddFile.Error(nil) + ) + + t = tar.NewWriter(w) + + for i := 0; i < len(content); i++ { + if content[i] == "" { + continue + } + + err = filepath.Walk(content[i], func(file string, inf os.FileInfo, err error) error { + var ( + e error + h *tar.Header + f *os.File + ) + + // generate tar header + h, e = tar.FileInfoHeader(inf, file) + if e != nil { + return e + } + + // must provide real name + // (see https://golang.org/src/archive/tar/common.go?#L626) + h.Name = filepath.ToSlash(file) + + // write header + if e = t.WriteHeader(h); e != nil { + return e + } + + // if not a dir, write file content + if !inf.IsDir() { + //nolint #gosec + /* #nosec */ + f, e = os.Open(file) + + if e != nil { + return e + } + + if _, e = io.Copy(t, f); e != nil { + return e + } + } + + n++ + return nil + }) + + if err != nil { + lEr.AddParent(err) + continue + } + } + + if n < 1 { + if lEr.HasParent() { + return false, lEr + } + + //nolint #goerr113 + return false, ErrorTarCreate.ErrorParent(fmt.Errorf("no file to add in archive")) + } else if !lEr.HasParent() { + lEr = nil + } + + if err = t.Close(); err != nil { + return false, ErrorTarCreate.ErrorParent(err) + } + return true, lEr } diff --git a/artifact/jfrog/model.go b/artifact/jfrog/model.go index 87fc0c4..22182ca 100644 --- a/artifact/jfrog/model.go +++ b/artifact/jfrog/model.go @@ -107,23 +107,14 @@ func (a *artifactoryModel) request(uri string, bodyResponse interface{}) liberr. u *url.URL ) + //ctx, cnl = context.WithTimeout(a.ctx, libhtc.TIMEOUT_5_SEC) + ctx, cnl = context.WithCancel(a.ctx) defer func() { if cnl != nil { cnl() } - - if rsp != nil && rsp.Body != nil { - _ = rsp.Body.Close() - } - - if req != nil && req.Body != nil { - _ = req.Body.Close() - } }() - //ctx, cnl = context.WithTimeout(a.ctx, libhtc.TIMEOUT_5_SEC) - ctx, cnl = context.WithCancel(a.ctx) - u = &url.URL{ Scheme: a.endpoint.Scheme, Opaque: a.endpoint.Opaque, @@ -149,15 +140,29 @@ func (a *artifactoryModel) request(uri string, bodyResponse interface{}) liberr. return ErrorRequestInit.ErrorParent(e) } + defer func() { + if req != nil && req.Body != nil { + _ = req.Body.Close() + } + }() + if rsp, e = a.Do(req); e != nil { return ErrorRequestDo.ErrorParent(e) } - if rsp.StatusCode >= 400 { + defer func() { + if rsp != nil && rsp.Body != nil { + _ = rsp.Body.Close() + } + }() + + if rsp.StatusCode >= http.StatusBadRequest { + //nolint #goerr113 return ErrorRequestResponse.ErrorParent(fmt.Errorf("status: %v", rsp.Status)) } if rsp.Body == nil { + //nolint #goerr113 return ErrorRequestResponseBodyEmpty.ErrorParent(fmt.Errorf("status: %v", rsp.Status)) } @@ -181,10 +186,12 @@ func (a *artifactoryModel) getStorageList() (sto []ResponseStorage, err liberr.E ) if a.regex == "" { + //nolint #goerr113 return nil, ErrorParamsEmpty.ErrorParent(fmt.Errorf("regex is empty: %s", a.regex)) } if a.group < 1 { + //nolint #goerr113 return nil, ErrorParamsEmpty.ErrorParent(fmt.Errorf("group extracted from regex is empty: %s - %v", a.regex, a.group)) } @@ -342,9 +349,11 @@ func (a *artifactoryModel) Download(dst libiot.FileProgress, containName string, return ErrorRequestInit.ErrorParent(e) } else if rsp, e = a.Do(req); e != nil { return ErrorRequestDo.ErrorParent(e) - } else if rsp.StatusCode >= 400 { + } else if rsp.StatusCode >= http.StatusBadRequest { + //nolint #goerr113 return ErrorRequestResponse.ErrorParent(fmt.Errorf("status: %v", rsp.Status)) } else if rsp.Body == nil { + //nolint #goerr113 return ErrorRequestResponseBodyEmpty.ErrorParent(fmt.Errorf("status: %v", rsp.Status)) } else if _, e := dst.ReadFrom(rsp.Body); e != nil { return ErrorArtifactoryDownload.ErrorParent(e) diff --git a/aws/aws_suite_test.go b/aws/aws_suite_test.go index 7ef3234..4a112c1 100644 --- a/aws/aws_suite_test.go +++ b/aws/aws_suite_test.go @@ -71,8 +71,6 @@ func TestGolibAwsHelper(t *testing.T) { RunSpecs(t, "Aws Helper Suite") } -const testRegion = "us-east-1" - var _ = BeforeSuite(func() { var ( err error diff --git a/aws/configCustom/models.go b/aws/configCustom/models.go index a463a8c..c7fd1db 100644 --- a/aws/configCustom/models.go +++ b/aws/configCustom/models.go @@ -226,7 +226,7 @@ func (c *awsModel) ResolveEndpoint(service, region string) (sdkaws.Endpoint, err }, nil } - logger.DebugLevel.Logf("Called ResolveEndpoint for service / region '%s' with nil endpoint", service, region) + logger.DebugLevel.Logf("Called ResolveEndpoint for service '%s' / region '%s' with nil endpoint", service, region) return sdkaws.Endpoint{}, ErrorEndpointInvalid.Error(nil) } diff --git a/aws/object/multipart.go b/aws/object/multipart.go index 9050f55..f870afb 100644 --- a/aws/object/multipart.go +++ b/aws/object/multipart.go @@ -26,10 +26,13 @@ package object import ( + //nolint #nosec /* #nosec */ "crypto/md5" "encoding/base64" "io" + + //nolint #gci "os" sdkaws "github.com/aws/aws-sdk-go-v2/aws" @@ -101,8 +104,8 @@ func (cli *client) MultipartPutCustom(partSize libhlp.PartSize, object string, b /* #nosec */ h := md5.New() - if _, err := tmp.WriteTo(h); err != nil { - return cli.multipartCancel(err, upl.UploadId, object) + if _, e := tmp.WriteTo(h); e != nil { + return cli.multipartCancel(e, upl.UploadId, object) } _, err = tmp.Seek(0, io.SeekStart) diff --git a/aws/policy/versions.go b/aws/policy/versions.go index b994f6d..b01e638 100644 --- a/aws/policy/versions.go +++ b/aws/policy/versions.go @@ -42,6 +42,7 @@ const maxItemList int32 = 1000 func (cli *client) VersionList(arn string, maxItem int32) (map[string]string, liberr.Error) { if arn == "" { + //nolint #goerr113 return nil, libhlp.ErrorParamsEmpty.ErrorParent(fmt.Errorf("arn is empty")) } @@ -51,7 +52,7 @@ func (cli *client) VersionList(arn string, maxItem int32) (map[string]string, li var ( marker = "" - res = make(map[string]string, 0) + res = make(map[string]string) ) for { diff --git a/certificates/config.go b/certificates/config.go index 5df5bb9..bf73e2a 100644 --- a/certificates/config.go +++ b/certificates/config.go @@ -39,12 +39,6 @@ type Certif struct { } type Config struct { - InheritDefault bool `mapstructure:"inheritDefault" json:"inheritDefault" yaml:"inheritDefault" toml:"inheritDefault"` - VersionMin string `mapstructure:"versionMin" json:"versionMin" yaml:"versionMin" toml:"versionMin"` - VersionMax string `mapstructure:"versionMax" json:"versionMax" yaml:"versionMax" toml:"versionMax"` - DynmaicSizingDisable bool `mapstructure:"dynamicSizingDisable" json:"dynamicSizingDisable" yaml:"dynamicSizingDisable" toml:"dynamicSizingDisable"` - SessionTicketDisable bool `mapstructure:"sessionTicketDisable" json:"sessionTicketDisable" yaml:"sessionTicketDisable" toml:"sessionTicketDisable"` - AuthClient string `mapstructure:"authClient" json:"authClient" yaml:"authClient" toml:"authClient"` CurveList []string `mapstructure:"curveList" json:"curveList" yaml:"curveList" toml:"curveList"` CipherList []string `mapstructure:"cipherList" json:"cipherList" yaml:"cipherList" toml:"cipherList"` RootCAString []string `mapstructure:"rootCA" json:"rootCA" yaml:"rootCA" toml:"rootCA"` @@ -53,6 +47,12 @@ type Config struct { ClientCAFiles []string `mapstructure:"clientCAFiles" json:"clientCAFiles" yaml:"clientCAFiles" toml:"clientCAFiles"` CertPairString []Certif `mapstructure:"certPair" json:"certPair" yaml:"certPair" toml:"certPair"` CertPairFile []Certif `mapstructure:"certPairFiles" json:"certPairFiles" yaml:"certPairFiles" toml:"certPairFiles"` + VersionMin string `mapstructure:"versionMin" json:"versionMin" yaml:"versionMin" toml:"versionMin"` + VersionMax string `mapstructure:"versionMax" json:"versionMax" yaml:"versionMax" toml:"versionMax"` + AuthClient string `mapstructure:"authClient" json:"authClient" yaml:"authClient" toml:"authClient"` + InheritDefault bool `mapstructure:"inheritDefault" json:"inheritDefault" yaml:"inheritDefault" toml:"inheritDefault"` + DynamicSizingDisable bool `mapstructure:"dynamicSizingDisable" json:"dynamicSizingDisable" yaml:"dynamicSizingDisable" toml:"dynamicSizingDisable"` + SessionTicketDisable bool `mapstructure:"sessionTicketDisable" json:"sessionTicketDisable" yaml:"sessionTicketDisable" toml:"sessionTicketDisable"` } func (c *Config) Validate() liberr.Error { @@ -64,6 +64,7 @@ func (c *Config) Validate() liberr.Error { } for _, err := range err.(valid.ValidationErrors) { + //nolint #goerr113 e.AddParent(fmt.Errorf("config field '%s' is not validated by constraint '%s'", err.StructNamespace(), err.ActualTag())) } } @@ -83,6 +84,7 @@ func (c *Config) New() (TLSConfig, liberr.Error) { } } +//nolint #gocognit func (c *Config) NewFrom(cfg TLSConfig) (TLSConfig, liberr.Error) { var t *config @@ -103,7 +105,7 @@ func (c *Config) NewFrom(cfg TLSConfig) (TLSConfig, liberr.Error) { t.tlsMaxVersion = StringToTlsVersion(c.VersionMax) } - if c.DynmaicSizingDisable { + if c.DynamicSizingDisable { t.dynSizingDisabled = true } diff --git a/certificates/interface.go b/certificates/interface.go index 968b533..a58f1bd 100644 --- a/certificates/interface.go +++ b/certificates/interface.go @@ -80,47 +80,47 @@ func New() TLSConfig { } } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func AddRootCAContents(rootContent string) bool { return Default.AddRootCAString(rootContent) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func AddRootCAFile(rootFile string) liberr.Error { return Default.AddRootCAFile(rootFile) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func AddCACertificateContents(caContent string) bool { return Default.AddClientCAString(caContent) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func AddCACertificateFile(caFile string) liberr.Error { return Default.AddClientCAFile(caFile) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func AddCertificatePairString(key, crt string) liberr.Error { return Default.AddCertificatePairString(key, crt) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func AddCertificatePairFile(keyFile, crtFile string) liberr.Error { return Default.AddCertificatePairFile(keyFile, crtFile) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func CheckCertificates() bool { return Default.LenCertificatePair() > 0 } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func GetCertificates() []tls.Certificate { return Default.GetCertificatePair() } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func AppendCertificates(cert []tls.Certificate) []tls.Certificate { if !CheckCertificates() { return cert @@ -129,67 +129,67 @@ func AppendCertificates(cert []tls.Certificate) []tls.Certificate { return append(cert, GetCertificates()...) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func GetRootCA() *x509.CertPool { return Default.GetRootCA() } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func GetClientCA() *x509.CertPool { return Default.GetClientCA() } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func SetVersionMin(vers uint16) { Default.SetVersionMin(vers) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func SetVersionMax(vers uint16) { Default.SetVersionMax(vers) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func SetClientAuth(auth string) { Default.SetClientAuth(StringToClientAuth(auth)) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func SetCipherList(cipher []uint16) { Default.SetCipherList(cipher) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func SetCurve(curves []tls.CurveID) { Default.SetCurveList(curves) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func SetDynamicSizing(enable bool) { Default.SetDynamicSizingDisabled(!enable) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func SetSessionTicket(enable bool) { Default.SetSessionTicketDisabled(!enable) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func GetTLSConfig(serverName string) *tls.Config { return Default.TlsConfig(serverName) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func GetTlsConfigCertificates() *tls.Config { return Default.TlsConfig("") } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func AddCertificateContents(keyContents, certContents string) liberr.Error { return Default.AddCertificatePairString(keyContents, certContents) } -// Deprecated: use local config and no more globals default config +// Deprecated: use local config and no more globals default config. func AddCertificateFile(keyFile, certFile string) liberr.Error { return Default.AddCertificatePairFile(keyFile, certFile) } diff --git a/certificates/model.go b/certificates/model.go index 1bfd26a..7cf8061 100644 --- a/certificates/model.go +++ b/certificates/model.go @@ -38,20 +38,16 @@ import ( ) type config struct { - caRoot *x509.CertPool - cert []tls.Certificate - - tlsMinVersion uint16 - tlsMaxVersion uint16 - - cipherList []uint16 - curveList []tls.CurveID - + cert []tls.Certificate + cipherList []uint16 + curveList []tls.CurveID + caRoot *x509.CertPool + clientAuth tls.ClientAuthType + clientCA *x509.CertPool + tlsMinVersion uint16 + tlsMaxVersion uint16 dynSizingDisabled bool ticketSessionDisabled bool - - clientAuth tls.ClientAuthType - clientCA *x509.CertPool } func (c *config) checkFile(pemFiles ...string) liberr.Error { @@ -248,12 +244,7 @@ func (c *config) cloneCipherList() []uint16 { return nil } - list := make([]uint16, 0) - for _, v := range c.cipherList { - list = append(list, v) - } - - return list + return append(make([]uint16, 0), c.cipherList...) } func (c *config) cloneCurveList() []tls.CurveID { @@ -261,12 +252,7 @@ func (c *config) cloneCurveList() []tls.CurveID { return nil } - list := make([]tls.CurveID, 0) - for _, v := range c.curveList { - list = append(list, v) - } - - return list + return append(make([]tls.CurveID, 0), c.curveList...) } func (c *config) cloneCertificates() []tls.Certificate { @@ -274,12 +260,7 @@ func (c *config) cloneCertificates() []tls.Certificate { return nil } - list := make([]tls.Certificate, 0) - for _, v := range c.cert { - list = append(list, v) - } - - return list + return append(make([]tls.Certificate, 0), c.cert...) } func (c *config) cloneRootCA() *x509.CertPool { diff --git a/certificates/tools.go b/certificates/tools.go index d5cb50d..c65274a 100644 --- a/certificates/tools.go +++ b/certificates/tools.go @@ -33,12 +33,12 @@ import ( "strings" ) -// Deprecated: use StringToCipherKey +// Deprecated: use StringToCipherKey. func GetCipherKey(cipher string) uint16 { return StringToCipherKey(cipher) } -// Deprecated: use StringToCurveID +// Deprecated: use StringToCurveID. func GetCurveID(curveRef string) tls.CurveID { return StringToCurveID(curveRef) } diff --git a/cluster/configCluster.go b/cluster/configCluster.go index 3086947..997411d 100644 --- a/cluster/configCluster.go +++ b/cluster/configCluster.go @@ -223,6 +223,7 @@ func (c ConfigCluster) GetDGBConfigCluster() dgbcfg.Config { d.Quiesce = true } + //nolint #exhaustive switch c.SnapshotCompressionType { case dgbcfg.Snappy: d.SnapshotCompressionType = dgbcfg.Snappy @@ -230,6 +231,7 @@ func (c ConfigCluster) GetDGBConfigCluster() dgbcfg.Config { d.SnapshotCompressionType = dgbcfg.NoCompression } + //nolint #exhaustive switch c.EntryCompressionType { case dgbcfg.Snappy: d.EntryCompressionType = dgbcfg.Snappy diff --git a/console/prompt.go b/console/prompt.go index 046b5e0..c3f572b 100644 --- a/console/prompt.go +++ b/console/prompt.go @@ -33,7 +33,7 @@ import ( "strconv" "syscall" - "golang.org/x/crypto/ssh/terminal" + "golang.org/x/term" ) func printPrompt(text string) { @@ -87,7 +87,7 @@ func PromptBool(text string) (bool, error) { func PromptPassword(text string) (string, error) { printPrompt(text) //nolint #unconvert - res, err := terminal.ReadPassword(int(syscall.Stdin)) + res, err := term.ReadPassword(int(syscall.Stdin)) fmt.Printf("\n") return string(res), err diff --git a/context/config.go b/context/config.go index be05a22..053a651 100644 --- a/context/config.go +++ b/context/config.go @@ -36,39 +36,43 @@ type Config interface { Store(key string, cfg interface{}) Load(key string) interface{} - - ObjectStore(key string, obj interface{}) - ObjectLoad(key string) interface{} } func NewConfig(ctx context.Context) Config { return &configContext{ Context: ctx, - cfg: &atomic.Value{}, - obj: &atomic.Value{}, + cfg: new(atomic.Value), } } type configContext struct { context.Context cfg *atomic.Value - obj *atomic.Value } -func (c *configContext) Store(key string, cfg interface{}) { +func (c configContext) getMap() map[string]*atomic.Value { var ( v interface{} s map[string]*atomic.Value ok bool ) - if v = c.cfg.Load(); v == nil { - s = make(map[string]*atomic.Value, 0) + if c.cfg == nil { + c.cfg = new(atomic.Value) + } else if v = c.cfg.Load(); v == nil { + s = make(map[string]*atomic.Value) + } else if s, ok = v.(map[string]*atomic.Value); !ok { - s = make(map[string]*atomic.Value, 0) + s = make(map[string]*atomic.Value) } - if _, ok = s[key]; !ok { + return s +} + +func (c *configContext) Store(key string, cfg interface{}) { + s := c.getMap() + + if _, ok := s[key]; !ok { s[key] = &atomic.Value{} } @@ -77,17 +81,9 @@ func (c *configContext) Store(key string, cfg interface{}) { } func (c *configContext) Load(key string) interface{} { - var ( - v interface{} - s map[string]*atomic.Value - ok bool - ) + s := c.getMap() - if v = c.cfg.Load(); v == nil { - return nil - } else if s, ok = v.(map[string]*atomic.Value); !ok { - return nil - } else if _, ok = s[key]; !ok { + if _, ok := s[key]; !ok { return nil } else { return s[key].Load() @@ -97,10 +93,6 @@ func (c *configContext) Load(key string) interface{} { func (c *configContext) Merge(cfg Config) bool { var ( x *configContext - vx interface{} - ix interface{} - sc map[string]*atomic.Value - sx map[string]*atomic.Value ok bool ) @@ -108,68 +100,32 @@ func (c *configContext) Merge(cfg Config) bool { return false } - sc = make(map[string]*atomic.Value, 0) + s := c.getMap() - if vx = x.cfg.Load(); vx == nil { - sx = make(map[string]*atomic.Value, 0) - } else if sx, ok = vx.(map[string]*atomic.Value); !ok { - sx = make(map[string]*atomic.Value, 0) - } - - for k, v := range sx { + for k, v := range x.getMap() { if k == "" || v == nil { continue } - ix = v.Load() - if ix == nil { + if i := v.Load(); i == nil { continue + } else { + s[k] = &atomic.Value{} + s[k].Store(i) } - - sc[k] = &atomic.Value{} - sc[k].Store(ix) } - c.cfg.Store(sc) + c.cfg.Store(s) return true } +//Deprecated: use Store. func (c *configContext) ObjectStore(key string, obj interface{}) { - var ( - v interface{} - s map[string]*atomic.Value - ok bool - ) - - if v = c.obj.Load(); v == nil { - s = make(map[string]*atomic.Value, 0) - } else if s, ok = v.(map[string]*atomic.Value); !ok { - s = make(map[string]*atomic.Value, 0) - } - - if _, ok = s[key]; !ok { - s[key] = &atomic.Value{} - } - - s[key].Store(obj) - c.obj.Store(s) + c.Store(key, obj) } +//Deprecated: use Load. func (c *configContext) ObjectLoad(key string) interface{} { - var ( - v interface{} - s map[string]*atomic.Value - ok bool - ) - - if v = c.obj.Load(); v == nil { - return nil - } else if s, ok = v.(map[string]*atomic.Value); !ok { - return nil - } else if _, ok = s[key]; !ok { - return nil - } else { - return s[key].Load() - } + return c.Load(key) } diff --git a/context/context.go b/context/context.go index 5201210..d80a429 100644 --- a/context/context.go +++ b/context/context.go @@ -27,6 +27,7 @@ package context import "context" func IsolateParent(parent context.Context) context.Context { + //nolint #govet x, _ := context.WithCancel(parent) return x } diff --git a/context/ginTonic.go b/context/ginTonic.go index 0100321..2834883 100644 --- a/context/ginTonic.go +++ b/context/ginTonic.go @@ -30,9 +30,8 @@ import ( "os/signal" "time" - "github.com/nabbar/golib/logger" - "github.com/gin-gonic/gin" + liblog "github.com/nabbar/golib/logger" ) type GinTonic interface { @@ -102,11 +101,11 @@ func (c *ctxGinTonic) CancelOnSignal(s ...os.Signal) { select { case <-sc: - logger.InfoLevel.Logf("Os Signal recieved, calling context cancel !") + liblog.InfoLevel.Logf("Os Signal received, calling context cancel !") c.c() return case <-c.Done(): - logger.InfoLevel.Logf("Context has been closed...") + liblog.InfoLevel.Logf("Context has been closed...") return } }() diff --git a/errors/errors.go b/errors/errors.go index 69043fb..ee5fb8d 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -91,7 +91,7 @@ type Error interface { IsCodeError(code CodeError) bool //HasCodeError check if current error or parent has the given error code HasCodeError(code CodeError) bool - //GetCodeError return the CodeError value of the curent error + //GetCodeError return the CodeError value of the current error GetCodeError() CodeError //GetCodeErrorParent return a slice of CodeError value of all parent Error and the code of the current Error GetCodeErrorParent() []CodeError diff --git a/errors/return.go b/errors/return.go index 017c328..5912099 100644 --- a/errors/return.go +++ b/errors/return.go @@ -60,7 +60,7 @@ func (r *DefaultReturn) SetError(code int, msg string, file string, line int) { } func (r *DefaultReturn) AddParent(code int, msg string, file string, line int) { - return + // nothing } func (r DefaultReturn) JSON() []byte { @@ -89,6 +89,7 @@ func (r DefaultReturn) GinTonicErrorAbort(ctx *gin.Context, httpCode int) { } ctx.Errors = append(ctx.Errors, &gin.Error{ + //nolint #goerr113 Err: goErr.New(r.Message), Type: gin.ErrorTypeAny, }) diff --git a/go.mod b/go.mod index 8e01db5..1ead2eb 100644 --- a/go.mod +++ b/go.mod @@ -3,37 +3,93 @@ module github.com/nabbar/golib go 1.16 require ( + github.com/DataDog/zstd v1.4.8 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver v1.5.0 // indirect + github.com/Masterminds/sprig v2.22.0+incompatible // indirect + github.com/PuerkitoBio/goquery v1.6.1 // indirect github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect + github.com/VictoriaMetrics/metrics v1.17.2 // indirect + github.com/andybalholm/cascadia v1.2.0 // indirect + github.com/aokoli/goutils v1.1.1 // indirect + github.com/armon/go-metrics v0.3.6 // indirect github.com/aws/aws-sdk-go-v2 v1.3.2 github.com/aws/aws-sdk-go-v2/config v1.1.5 github.com/aws/aws-sdk-go-v2/credentials v1.1.5 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.6 // indirect github.com/aws/aws-sdk-go-v2/service/iam v1.3.1 + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.0.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.2.2 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.5.0 + github.com/aws/aws-sdk-go-v2/service/sso v1.1.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.2.2 // indirect + github.com/cockroachdb/errors v1.8.3 // indirect + github.com/cockroachdb/pebble v0.0.0-20210406003833-3d4c32f510a8 // indirect + github.com/cockroachdb/redact v1.0.9 // indirect github.com/fatih/color v1.10.0 github.com/fxamacker/cbor/v2 v2.2.0 github.com/gin-gonic/gin v1.7.1 + github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect github.com/go-ldap/ldap/v3 v3.3.0 github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-playground/validator/v10 v10.5.0 + github.com/gobuffalo/envy v1.9.0 // indirect + github.com/gobuffalo/packd v1.0.0 // indirect github.com/gobuffalo/packr v1.30.1 + github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.3 // indirect + github.com/google/btree v1.0.1 // indirect github.com/google/go-github/v33 v33.0.0 + github.com/google/go-querystring v1.1.0 // indirect + github.com/google/uuid v1.2.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-immutable-radix v1.3.0 // indirect + github.com/hashicorp/go-msgpack v1.1.5 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.6.8 + github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/go-uuid v1.0.2 github.com/hashicorp/go-version v1.3.0 + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/memberlist v0.2.3 // indirect + github.com/huandu/xstrings v1.3.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 // indirect + github.com/json-iterator/go v1.1.10 // indirect + github.com/klauspost/compress v1.11.13 // indirect + github.com/kr/pretty v0.2.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/leodido/go-urn v1.2.1 // indirect github.com/lni/dragonboat/v3 v3.3.2 github.com/matcornic/hermes/v2 v2.1.0 + github.com/mattn/go-runewidth v0.0.12 // indirect + github.com/miekg/dns v1.1.41 // indirect + github.com/mitchellh/copystructure v1.1.2 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onsi/ginkgo v1.16.1 github.com/onsi/gomega v1.11.0 + github.com/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.8.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v3.21.3+incompatible github.com/sirupsen/logrus v1.8.1 github.com/spf13/jwalterweatherman v1.1.0 + github.com/ugorji/go v1.2.5 // indirect + github.com/vanng822/go-premailer v1.20.1 // indirect github.com/vbauerster/mpb/v5 v5.4.0 github.com/xanzy/go-gitlab v0.48.0 github.com/xhit/go-simple-mail v2.2.2+incompatible github.com/xujiajun/nutsdb v0.6.0 github.com/xujiajun/utils v0.0.0-20190123093513-8bf096c4f53b - golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 + golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect + golang.org/x/exp v0.0.0-20210405174845-4513512abef3 // indirect golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750 // indirect + golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72 + golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect + google.golang.org/appengine v1.6.7 // indirect ) diff --git a/httpcli/cli.go b/httpcli/cli.go index 3e3b8f6..aa11403 100644 --- a/httpcli/cli.go +++ b/httpcli/cli.go @@ -64,7 +64,8 @@ func GetClientTimeout(serverName string, http2Tr bool, GlobalTimeout time.Durati Proxy: http.ProxyFromEnvironment, DialContext: dl.DialContext, DisableCompression: true, - TLSClientConfig: certificates.GetTLSConfig(serverName), + //nolint #staticcheck + TLSClientConfig: certificates.GetTLSConfig(serverName), } return getclient(tr, http2Tr, GlobalTimeout) diff --git a/httpserver/config.go b/httpserver/config.go index 73dc844..c454d17 100644 --- a/httpserver/config.go +++ b/httpserver/config.go @@ -35,7 +35,6 @@ import ( "time" "github.com/go-playground/validator/v10" - libtls "github.com/nabbar/golib/certificates" liberr "github.com/nabbar/golib/errors" ) @@ -129,10 +128,37 @@ func (p PoolServerConfig) MapRun(f MapRunPoolServerConfig) PoolServerConfig { return r } +//nolint #maligned type ServerConfig struct { - getTLSDefault func() libtls.TLSConfig + + // Name is the name of the current server + // the configuration allow multipke server, which each one must be identify by a name + // If not defined, will use the listen address + Name string `mapstructure:"name" json:"name" yaml:"name" toml:"name" validate:"required"` + + // Listen is the local address (ip, hostname, unix socket, ...) with a port + // The server will bind with this address only and listen for the port defined + Listen string `mapstructure:"listen" json:"listen" yaml:"listen" toml:"listen" validate:"required,hostname_port"` + + // Expose is the address use to call this server. This can be allow to use a single fqdn to multiple server" + Expose string `mapstructure:"expose" json:"expose" yaml:"expose" toml:"expose" validate:"required,url"` + + // HandlerKeys is an options to associate current server with a specifc handler defined by the key + // This allow to defined multiple server in only one config for different handler to start multiple api + HandlerKeys string `mapstructure:"handler_keys" json:"handler_keys" yaml:"handler_keys" toml:"handler_keys"` + + //private + getTLSDefault func() libtls.TLSConfig + + //private getParentContext func() context.Context + // TimeoutCacheInfo defined the validity time of cache for info (name, version, hash) + TimeoutCacheInfo time.Duration `mapstructure:"timeout_cache_info" json:"timeout_cache_info" yaml:"timeout_cache_info" toml:"timeout_cache_info"` + + // TimeoutCacheHealth defined the validity time of cache for healthcheck of this server + TimeoutCacheHealth time.Duration `mapstructure:"timeout_cache_health" json:"timeout_cache_health" yaml:"timeout_cache_health" toml:"timeout_cache_health"` + // Enabled allow to disable a server without clean his configuration Disabled bool `mapstructure:"disabled" json:"disabled" yaml:"disabled" toml:"disabled"` @@ -140,11 +166,13 @@ type ServerConfig struct { // y defined if the component for status is mandatory or not Mandatory bool `mapstructure:"mandatory" json:"mandatory" yaml:"mandatory" toml:"mandatory"` - // TimeoutCacheInfo defined the validity time of cache for info (name, version, hash) - TimeoutCacheInfo time.Duration `mapstructure:"timeout_cache_info" json:"timeout_cache_info" yaml:"timeout_cache_info" toml:"timeout_cache_info"` + // TLSMandatory is a flag to defined that TLS must be valid to start current server. + TLSMandatory bool `mapstructure:"tls_mandatory" json:"tls_mandatory" yaml:"tls_mandatory" toml:"tls_mandatory"` - // TimeoutCacheHealth defined the validity time of cache for healthcheck of this server - TimeoutCacheHealth time.Duration `mapstructure:"timeout_cache_health" json:"timeout_cache_health" yaml:"timeout_cache_health" toml:"timeout_cache_health"` + // TLS is the tls configuration for this server. + // To allow tls on this server, at least the TLS Config option InheritDefault must be at true and the default TLS config must be set. + // If you don't want any tls config, just omit or set an empty struct. + TLS libtls.Config `mapstructure:"tls" json:"tls" yaml:"tls" toml:"tls"` /*** http options ***/ @@ -220,30 +248,6 @@ type ServerConfig struct { // be larger than 2^32-1. If the value is zero or larger than the // maximum, a default value will be used instead. MaxUploadBufferPerStream int32 `json:"max_upload_buffer_per_stream" json:"max_upload_buffer_per_stream" yaml:"max_upload_buffer_per_stream" toml:"max_upload_buffer_per_stream"` - - // Name is the name of the current server - // the configuration allow multipke server, which each one must be identify by a name - // If not defined, will use the listen address - Name string `mapstructure:"name" json:"name" yaml:"name" toml:"name" validate:"required"` - - // Listen is the local address (ip, hostname, unix socket, ...) with a port - // The server will bind with this address only and listen for the port defined - Listen string `mapstructure:"listen" json:"listen" yaml:"listen" toml:"listen" validate:"required,hostname_port"` - - // Expose is the address use to call this server. This can be allow to use a single fqdn to multiple server" - Expose string `mapstructure:"expose" json:"expose" yaml:"expose" toml:"expose" validate:"required,url"` - - // HandlerKeys is an options to associate current server with a specifc handler defined by the key - // This allow to defined multiple server in only one config for different handler to start multiple api - HandlerKeys string `mapstructure:"handler_keys" json:"handler_keys" yaml:"handler_keys" toml:"handler_keys"` - - // TLSMandatory is a flag to defined that TLS must be valid to start current server. - TLSMandatory bool `mapstructure:"tls_mandatory" json:"tls_mandatory" yaml:"tls_mandatory" toml:"tls_mandatory"` - - // TLS is the tls configuration for this server. - // To allow tls on this server, at least the TLS Config option InheritDefault must be at true and the default TLS config must be set. - // If you don't want any tls config, just omit or set an empty struct. - TLS libtls.Config `mapstructure:"tls" json:"tls" yaml:"tls" toml:"tls"` } func (c *ServerConfig) Clone() ServerConfig { @@ -319,7 +323,7 @@ func (c ServerConfig) GetListen() *url.URL { if c.Listen != "" { if add, err = url.Parse(c.Listen); err != nil { - if host, prt, err := net.SplitHostPort(c.Listen); err == nil { + if host, prt, e := net.SplitHostPort(c.Listen); e == nil { add = &url.URL{ Host: fmt.Sprintf("%s:%s", host, prt), } diff --git a/httpserver/pool.go b/httpserver/pool.go index 22f5c3e..e312a7c 100644 --- a/httpserver/pool.go +++ b/httpserver/pool.go @@ -36,13 +36,10 @@ import ( "strings" "syscall" - "github.com/nabbar/golib/status" - - "github.com/nabbar/golib/logger" - - "github.com/nabbar/golib/semaphore" - liberr "github.com/nabbar/golib/errors" + liblog "github.com/nabbar/golib/logger" + libsem "github.com/nabbar/golib/semaphore" + libsts "github.com/nabbar/golib/status" ) type FieldType uint8 @@ -82,7 +79,7 @@ type PoolServer interface { StatusInfo(bindAddress string) (name string, release string, hash string) StatusHealth(bindAddress string) error - StatusRoute(prefix string, fctMessage status.FctMessage, sts status.RouteStatus) + StatusRoute(prefix string, fctMessage libsts.FctMessage, sts libsts.RouteStatus) } func NewPool(srv ...Server) PoolServer { @@ -353,7 +350,7 @@ func (p pool) ListenMultiHandler(handler map[string]http.Handler) liberr.Error { var e liberr.Error e = ErrorPoolListen.Error(nil) - logger.InfoLevel.Log("Calling listen for All Servers") + liblog.InfoLevel.Log("Calling listen for All Servers") p.MapRun(func(srv Server) { if len(handler) < 1 { @@ -371,7 +368,7 @@ func (p pool) ListenMultiHandler(handler map[string]http.Handler) liberr.Error { } }) - logger.InfoLevel.Log("End of Calling listen for All Servers") + liblog.InfoLevel.Log("End of Calling listen for All Servers") if !e.HasParent() { e = nil @@ -380,64 +377,66 @@ func (p pool) ListenMultiHandler(handler map[string]http.Handler) liberr.Error { return e } -func (p pool) Restart() { +func (p pool) runMapCommand(f func(sem libsem.Sem, srv Server)) { if p.Len() < 1 { return } var ( - s semaphore.Sem + s libsem.Sem x context.Context c context.CancelFunc ) + x, c = context.WithTimeout(context.Background(), timeoutShutdown) + defer func() { c() s.DeferMain() }() - x, c = context.WithTimeout(context.Background(), timeoutRestart) - s = semaphore.NewSemaphoreWithContext(x, 0) + s = libsem.NewSemaphoreWithContext(x, 0) p.MapRun(func(srv Server) { _ = s.NewWorker() - go func() { - defer s.DeferWorker() - srv.Restart() - }() + go func(sem libsem.Sem, srv Server) { + f(sem, srv) + }(s, srv) }) _ = s.WaitAll() } -func (p pool) Shutdown() { - if p.Len() < 1 { - return - } - - var ( - s semaphore.Sem - x context.Context - c context.CancelFunc - ) - +func (p pool) runMapRestart(sem libsem.Sem, srv Server) { defer func() { - c() - s.DeferMain() + if sem != nil { + sem.DeferWorker() + } }() - x, c = context.WithTimeout(context.Background(), timeoutShutdown) - s = semaphore.NewSemaphoreWithContext(x, 0) + if srv != nil { + srv.Restart() + } +} - p.MapRun(func(srv Server) { - _ = s.NewWorker() - go func() { - defer s.DeferWorker() - srv.Shutdown() - }() - }) +func (p pool) runMapShutdown(sem libsem.Sem, srv Server) { + defer func() { + if sem != nil { + sem.DeferWorker() + } + }() - _ = s.WaitAll() + if srv != nil { + srv.Shutdown() + } +} + +func (p pool) Restart() { + p.runMapCommand(p.runMapRestart) +} + +func (p pool) Shutdown() { + p.runMapCommand(p.runMapShutdown) } func (p pool) StatusInfo(bindAddress string) (name string, release string, hash string) { @@ -453,10 +452,11 @@ func (p pool) StatusHealth(bindAddress string) error { return s.StatusHealth() } + //nolint #goerr113 return fmt.Errorf("missing server '%s'", bindAddress) } -func (p pool) StatusRoute(keyPrefix string, fctMessage status.FctMessage, sts status.RouteStatus) { +func (p pool) StatusRoute(keyPrefix string, fctMessage libsts.FctMessage, sts libsts.RouteStatus) { p.MapRun(func(srv Server) { bind := srv.GetBindable() sts.ComponentNew(fmt.Sprintf("%s-%s", keyPrefix, bind), srv.StatusComponent(fctMessage)) diff --git a/httpserver/run.go b/httpserver/run.go index 4693014..38fa5bb 100644 --- a/httpserver/run.go +++ b/httpserver/run.go @@ -45,6 +45,8 @@ import ( "golang.org/x/net/http2" ) +const _TimeoutWaitingPortFreeing = 500 * time.Microsecond + type srvRun struct { err *atomic.Value run *atomic.Value @@ -109,6 +111,7 @@ func (s *srvRun) setErr(e error) { if e != nil { s.err.Store(e) } else { + //nolint #goerr113 s.err.Store(errors.New("")) } } @@ -141,6 +144,7 @@ func (s *srvRun) Merge(srv Server) bool { panic("implement me") } +//nolint #gocognit func (s *srvRun) Listen(cfg *ServerConfig, handler http.Handler) liberr.Error { ssl, err := cfg.GetTLS() if err != nil { @@ -263,6 +267,7 @@ func (s *srvRun) Listen(cfg *ServerConfig, handler http.Handler) liberr.Error { s.setRunning(true) err = s.srv.ListenAndServeTLS("", "") } else if tlsMandatory { + //nolint #goerr113 err = fmt.Errorf("missing valid server certificates") } else { liblog.InfoLevel.Logf("Server '%s' is starting with bindable: %s", name, host) @@ -333,7 +338,7 @@ func (s *srvRun) PortInUse(listen string) liberr.Error { } }() - ctx, cnl = context.WithTimeout(context.TODO(), 2*time.Second) + ctx, cnl = context.WithTimeout(context.TODO(), _TimeoutWaitingPortFreeing) con, err = dia.DialContext(ctx, "tcp", listen) if con != nil { diff --git a/httpserver/server.go b/httpserver/server.go index 7bf5ab1..bae03a4 100644 --- a/httpserver/server.go +++ b/httpserver/server.go @@ -34,24 +34,22 @@ import ( "sync/atomic" "time" - "github.com/nabbar/golib/status" - liberr "github.com/nabbar/golib/errors" + libsts "github.com/nabbar/golib/status" ) const ( timeoutShutdown = 10 * time.Second - timeoutRestart = 30 * time.Second ) type server struct { run *atomic.Value - cfg ServerConfig + cfg *atomic.Value } type Server interface { GetConfig() *ServerConfig - SetConfig(cfg *ServerConfig) + SetConfig(cfg *ServerConfig) bool GetName() string GetBindable() string @@ -69,16 +67,50 @@ type Server interface { StatusInfo() (name string, release string, hash string) StatusHealth() error - StatusComponent(message status.FctMessage) status.Component + StatusComponent(message libsts.FctMessage) libsts.Component } func NewServer(cfg *ServerConfig) Server { + c := new(atomic.Value) + c.Store(cfg.Clone()) + return &server{ - cfg: cfg.Clone(), + cfg: c, run: new(atomic.Value), } } +func (s *server) GetConfig() *ServerConfig { + if s.cfg == nil { + return nil + } else if i := s.cfg.Load(); i == nil { + return nil + } else if c, ok := i.(ServerConfig); !ok { + return nil + } else { + return &c + } +} + +func (s *server) SetConfig(cfg *ServerConfig) bool { + if cfg == nil { + return false + } + + if s.cfg == nil { + s.cfg = new(atomic.Value) + } + + c := cfg.Clone() + + if c.Name == "" { + c.Name = c.GetListen().Host + } + + s.cfg.Store(cfg.Clone()) + return true +} + func (s *server) getRun() run { if s.run == nil { return newRun() @@ -103,32 +135,20 @@ func (s *server) getErr() error { } } -func (s *server) GetConfig() *ServerConfig { - return &s.cfg -} - -func (s *server) SetConfig(cfg *ServerConfig) { - s.cfg = cfg.Clone() -} - func (s server) GetName() string { - if s.cfg.Name == "" { - s.cfg.Name = s.GetBindable() - } - - return s.cfg.Name + return s.GetConfig().Name } func (s *server) GetBindable() string { - return s.cfg.GetListen().Host + return s.GetConfig().GetListen().Host } func (s *server) GetExpose() string { - return s.cfg.GetExpose().String() + return s.GetConfig().GetExpose().String() } func (s *server) GetHandlerKey() string { - return s.cfg.GetHandlerKey() + return s.GetConfig().GetHandlerKey() } func (s *server) IsRunning() bool { @@ -136,12 +156,12 @@ func (s *server) IsRunning() bool { } func (s *server) IsTLS() bool { - return s.cfg.IsTLS() + return s.GetConfig().IsTLS() } func (s *server) Listen(handler http.Handler) liberr.Error { r := s.getRun() - e := r.Listen(&s.cfg, handler) + e := r.Listen(s.GetConfig(), handler) s.setRun(r) return e @@ -163,12 +183,11 @@ func (s *server) Shutdown() { } func (s *server) Merge(srv Server) bool { - if x, ok := srv.(*server); ok { - s.cfg = x.cfg - return true + if x, ok := srv.(*server); !ok { + return false + } else { + return s.SetConfig(x.GetConfig()) } - - return false } func (s *server) StatusInfo() (name string, release string, hash string) { @@ -180,17 +199,20 @@ func (s *server) StatusInfo() (name string, release string, hash string) { } func (s *server) StatusHealth() error { - if !s.cfg.Disabled && s.IsRunning() { + c := s.GetConfig() + if !c.Disabled && s.IsRunning() { return nil - } else if s.cfg.Disabled { + } else if c.Disabled { + //nolint #goerr113 return fmt.Errorf("server disabled") } else if e := s.getErr(); e != nil { return e } else { + //nolint #goerr113 return fmt.Errorf("server is offline -- missing error") } } -func (s *server) StatusComponent(message status.FctMessage) status.Component { - return status.NewComponent(s.cfg.Mandatory, s.StatusInfo, s.StatusHealth, message, s.cfg.TimeoutCacheInfo, s.cfg.TimeoutCacheHealth) +func (s *server) StatusComponent(message libsts.FctMessage) libsts.Component { + return libsts.NewComponent(s.GetConfig().Mandatory, s.StatusInfo, s.StatusHealth, message, s.GetConfig().TimeoutCacheInfo, s.GetConfig().TimeoutCacheHealth) } diff --git a/ldap/ldap.go b/ldap/ldap.go index 98a7f77..83b3b43 100644 --- a/ldap/ldap.go +++ b/ldap/ldap.go @@ -59,10 +59,11 @@ func NewLDAP(ctx context.Context, cnf *Config, attributes []string) (*HelperLDAP return &HelperLDAP{ Attributes: attributes, - tlsConfig: libcrt.GetTLSConfig(cnf.Uri), - tlsMode: tlsmode_init, - config: cnf.Clone(), - ctx: libctx.IsolateParent(ctx), + //nolint #staticcheck + tlsConfig: libcrt.GetTLSConfig(cnf.Uri), + tlsMode: _TLSModeInit, + config: cnf.Clone(), + ctx: libctx.IsolateParent(ctx), }, nil } @@ -83,15 +84,15 @@ func (lc *HelperLDAP) ForceTLSMode(tlsMode TLSMode, tlsConfig *tls.Config) { } switch tlsMode { - case TLSMODE_TLS: - lc.tlsMode = TLSMODE_TLS - case TLSMODE_STARTTLS: - lc.tlsMode = TLSMODE_STARTTLS - case TLSMODE_NONE: + case TLSModeTLS: + lc.tlsMode = TLSModeTLS + case TLSModeStarttls: + lc.tlsMode = TLSModeStarttls + case TLSModeNone: lc.tlsConfig = nil - lc.tlsMode = TLSMODE_NONE - case tlsmode_init: - lc.tlsMode = tlsmode_init + lc.tlsMode = TLSModeNone + case _TLSModeInit: + lc.tlsMode = _TLSModeInit } } @@ -175,7 +176,7 @@ func (lc *HelperLDAP) starttls(l *ldap.Conn) liberr.Error { func (lc *HelperLDAP) tryConnect() (TLSMode, liberr.Error) { if lc == nil { - return TLSMODE_NONE, ErrorEmptyParams.Error(nil) + return TLSModeNone, ErrorEmptyParams.Error(nil) } var ( @@ -192,32 +193,32 @@ func (lc *HelperLDAP) tryConnect() (TLSMode, liberr.Error) { if lc.config.Portldaps != 0 { l, err = lc.dialTLS() - liblog.DebugLevel.LogErrorCtxf(liblog.DebugLevel, "connecting ldap with tls mode '%s'", err, TLSMODE_TLS.String()) + liblog.DebugLevel.LogErrorCtxf(liblog.DebugLevel, "connecting ldap with tls mode '%s'", err, TLSModeTLS.String()) if err == nil { - return TLSMODE_TLS, nil + return TLSModeTLS, nil } } if lc.config.PortLdap == 0 { - return tlsmode_init, ErrorLDAPServerConfig.Error(nil) + return _TLSModeInit, ErrorLDAPServerConfig.Error(nil) } l, err = lc.dial() - liblog.DebugLevel.LogErrorCtxf(liblog.DebugLevel, "connecting ldap with tls mode '%s'", err, TLSMODE_NONE.String()) + liblog.DebugLevel.LogErrorCtxf(liblog.DebugLevel, "connecting ldap with tls mode '%s'", err, TLSModeNone.String()) if err != nil { - return tlsmode_init, err + return _TLSModeInit, err } err = lc.starttls(l) - liblog.DebugLevel.LogErrorCtxf(liblog.DebugLevel, "connecting ldap with tls mode '%s'", err, TLSMODE_STARTTLS.String()) + liblog.DebugLevel.LogErrorCtxf(liblog.DebugLevel, "connecting ldap with tls mode '%s'", err, TLSModeStarttls.String()) if err == nil { - return TLSMODE_STARTTLS, nil + return TLSModeStarttls, nil } - return TLSMODE_NONE, nil + return TLSModeNone, nil } func (lc *HelperLDAP) connect() liberr.Error { @@ -235,7 +236,7 @@ func (lc *HelperLDAP) connect() liberr.Error { err liberr.Error ) - if lc.tlsMode == tlsmode_init { + if lc.tlsMode == _TLSModeInit { m, e := lc.tryConnect() if e != nil { @@ -245,7 +246,7 @@ func (lc *HelperLDAP) connect() liberr.Error { lc.tlsMode = m } - if lc.tlsMode == TLSMODE_TLS { + if lc.tlsMode == TLSModeTLS { l, err = lc.dialTLS() if err != nil { if l != nil { @@ -255,7 +256,7 @@ func (lc *HelperLDAP) connect() liberr.Error { } } - if lc.tlsMode == TLSMODE_NONE || lc.tlsMode == TLSMODE_STARTTLS { + if lc.tlsMode == TLSModeNone || lc.tlsMode == TLSModeStarttls { l, err = lc.dial() if err != nil { if l != nil { @@ -265,7 +266,7 @@ func (lc *HelperLDAP) connect() liberr.Error { } } - if lc.tlsMode == TLSMODE_STARTTLS { + if lc.tlsMode == TLSModeStarttls { err = lc.starttls(l) if err != nil { if l != nil { @@ -346,7 +347,7 @@ func (lc *HelperLDAP) Connect() liberr.Error { return err } - liblog.DebugLevel.Logf("Bind success on LDAP server %s with tls mode '%s'", lc.config.ServerAddr(lc.tlsMode == TLSMODE_TLS), lc.tlsMode.String()) + liblog.DebugLevel.Logf("Bind success on LDAP server %s with tls mode '%s'", lc.config.ServerAddr(lc.tlsMode == TLSModeTLS), lc.tlsMode.String()) return nil } @@ -376,7 +377,7 @@ func (lc *HelperLDAP) runSearch(filter string, attributes []string) (*ldap.Searc return nil, ErrorLDAPSearch.ErrorParent(err) } - liblog.DebugLevel.Logf("Search success on server '%s' with tls mode '%s', with filter [%s] and attribute %v", lc.config.ServerAddr(lc.tlsMode == TLSMODE_TLS), lc.tlsMode.String(), filter, attributes) + liblog.DebugLevel.Logf("Search success on server '%s' with tls mode '%s', with filter [%s] and attribute %v", lc.config.ServerAddr(lc.tlsMode == TLSModeTLS), lc.tlsMode.String(), filter, attributes) return src, nil } @@ -405,46 +406,10 @@ func (lc *HelperLDAP) getUserName(username string) (string, liberr.Error) { //UserInfo used to retrieve the information of a given username. func (lc *HelperLDAP) UserInfo(username string) (map[string]string, liberr.Error) { - var ( - err liberr.Error - src *ldap.SearchResult - userRes map[string]string - ) - - if username, err = lc.getUserName(username); err != nil { - return nil, err - } - - userRes = make(map[string]string) - attributes := append(lc.Attributes, "cn") - - src, err = lc.runSearch(fmt.Sprintf(lc.config.FilterUser, userFieldUid, username), attributes) - - if err != nil { - return userRes, err - } - - if len(src.Entries) != 1 { - if len(src.Entries) > 1 { - return userRes, ErrorLDAPUserNotUniq.Error(nil) - } else { - return userRes, ErrorLDAPUserNotFound.Error(nil) - } - } - - for _, attr := range attributes { - userRes[attr] = src.Entries[0].GetAttributeValue(attr) - } - - if _, ok := userRes["DN"]; !ok { - userRes["DN"] = src.Entries[0].DN - } - - liblog.DebugLevel.Logf("Map info retrieve in ldap server '%s' with tls mode '%s' about user [%s] : %v", lc.config.ServerAddr(lc.tlsMode == TLSMODE_TLS), lc.tlsMode.String(), username, userRes) - return userRes, nil + return lc.UserInfoByField(username, userFieldUid) } -//UserInfo used to retrieve the information of a given username. +//UserInfoByField used to retrieve the information of a given username but use a given field to make the search. func (lc *HelperLDAP) UserInfoByField(username string, fieldOfUnicValue string) (map[string]string, liberr.Error) { var ( err liberr.Error @@ -481,39 +446,16 @@ func (lc *HelperLDAP) UserInfoByField(username string, fieldOfUnicValue string) userRes["DN"] = src.Entries[0].DN } - liblog.DebugLevel.Logf("Map info retrieve in ldap server '%s' with tls mode '%s' about user [%s] : %v", lc.config.ServerAddr(lc.tlsMode == TLSMODE_TLS), lc.tlsMode.String(), username, userRes) + liblog.DebugLevel.Logf("Map info retrieve in ldap server '%s' with tls mode '%s' about user [%s] : %v", lc.config.ServerAddr(lc.tlsMode == TLSModeTLS), lc.tlsMode.String(), username, userRes) return userRes, nil } //GroupInfo used to retrieve the information of a given group cn. func (lc *HelperLDAP) GroupInfo(groupname string) (map[string]interface{}, liberr.Error) { - var ( - err liberr.Error - src *ldap.SearchResult - grpInfo map[string]interface{} - ) - - src, err = lc.runSearch(fmt.Sprintf(lc.config.FilterGroup, groupFieldCN, groupname), []string{}) - if err != nil { - return grpInfo, err - } - - if len(src.Entries) == 0 { - return nil, ErrorLDAPGroupNotFound.Error(nil) - } - - grpInfo = make(map[string]interface{}, len(src.Entries[0].Attributes)) - for _, entry := range src.Entries { - for _, entryAttribute := range entry.Attributes { - grpInfo[entryAttribute.Name] = entryAttribute.Values - } - } - - liblog.DebugLevel.Logf("Info for group [%s] find on server '%s' with tls mode '%s' : %v", groupname, lc.config.ServerAddr(lc.tlsMode == TLSMODE_TLS), lc.tlsMode.String(), grpInfo) - return grpInfo, nil + return lc.GroupInfoByField(groupname, groupFieldCN) } -//GroupInfo used to retrieve the information of a given group cn. +//GroupInfoByField used to retrieve the information of a given group cn, but use a given field to make the search. func (lc *HelperLDAP) GroupInfoByField(groupname string, fieldForUnicValue string) (map[string]interface{}, liberr.Error) { var ( err liberr.Error @@ -537,7 +479,7 @@ func (lc *HelperLDAP) GroupInfoByField(groupname string, fieldForUnicValue strin } } - liblog.DebugLevel.Logf("Info for group [%s] find on server '%s' with tls mode '%s' : %v", groupname, lc.config.ServerAddr(lc.tlsMode == TLSMODE_TLS), lc.tlsMode.String(), grpInfo) + liblog.DebugLevel.Logf("Info for group [%s] find on server '%s' with tls mode '%s' : %v", groupname, lc.config.ServerAddr(lc.tlsMode == TLSModeTLS), lc.tlsMode.String(), grpInfo) return grpInfo, nil } @@ -562,13 +504,13 @@ func (lc *HelperLDAP) UserMemberOf(username string) ([]string, liberr.Error) { for _, entry := range src.Entries { for _, mmb := range entry.GetAttributeValues("memberOf") { - liblog.DebugLevel.Logf("Group find for uid '%s' on server '%s' with tls mode '%s' : %v", username, lc.config.ServerAddr(lc.tlsMode == TLSMODE_TLS), lc.tlsMode.String(), mmb) + liblog.DebugLevel.Logf("Group find for uid '%s' on server '%s' with tls mode '%s' : %v", username, lc.config.ServerAddr(lc.tlsMode == TLSModeTLS), lc.tlsMode.String(), mmb) mmo := lc.ParseEntries(mmb) grp = append(grp, mmo["cn"]...) } } - liblog.DebugLevel.Logf("Groups find for uid '%s' on server '%s' with tls mode '%s' : %v", username, lc.config.ServerAddr(lc.tlsMode == TLSMODE_TLS), lc.tlsMode.String(), grp) + liblog.DebugLevel.Logf("Groups find for uid '%s' on server '%s' with tls mode '%s' : %v", username, lc.config.ServerAddr(lc.tlsMode == TLSModeTLS), lc.tlsMode.String(), grp) return grp, nil } @@ -618,7 +560,7 @@ func (lc *HelperLDAP) UsersOfGroup(groupname string) ([]string, liberr.Error) { } } - liblog.DebugLevel.Logf("Member of groups [%s] find on server '%s' with tls mode '%s' : %v", groupname, lc.config.ServerAddr(lc.tlsMode == TLSMODE_TLS), lc.tlsMode.String(), grp) + liblog.DebugLevel.Logf("Member of groups [%s] find on server '%s' with tls mode '%s' : %v", groupname, lc.config.ServerAddr(lc.tlsMode == TLSModeTLS), lc.tlsMode.String(), grp) return grp, nil } diff --git a/ldap/model.go b/ldap/model.go index 8363323..c39b132 100644 --- a/ldap/model.go +++ b/ldap/model.go @@ -35,13 +35,13 @@ import ( type TLSMode uint8 const ( - tlsmode_init TLSMode = iota + _TLSModeInit TLSMode = iota //TLSModeNone no tls connection. - TLSMODE_NONE TLSMode = iota + 1 + TLSModeNone TLSMode = iota + 1 //TLSModeTLS strict tls connection. - TLSMODE_TLS - //TLSModeStartTLS starttls connection (tls into a no tls connection). - TLSMODE_STARTTLS + TLSModeTLS + //TLSModeStarttls starttls connection (tls into a no tls connection). + TLSModeStarttls ) const ( @@ -51,17 +51,17 @@ const ( func (m TLSMode) String() string { switch m { - case TLSMODE_STARTTLS: + case TLSModeStarttls: return "starttls" - case TLSMODE_TLS: + case TLSModeTLS: return "tls" - case TLSMODE_NONE: + case TLSModeNone: return "none" - case tlsmode_init: + case _TLSModeInit: return "not defined" } - return tlsmode_init.String() + return _TLSModeInit.String() } func GetDefaultAttributes() []string { @@ -123,6 +123,7 @@ func (cnf Config) Validate() errors.Error { } for _, err := range err.(validator.ValidationErrors) { + //nolint #goerr113 e.AddParent(fmt.Errorf("config field '%s' is not validated by constraint '%s'", err.StructNamespace(), err.ActualTag())) } } diff --git a/mail/base64.go b/mail/base64.go deleted file mode 100644 index 6e79859..0000000 --- a/mail/base64.go +++ /dev/null @@ -1,66 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020 Nicolas JUHEL - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package mail - -import "io" - -const maxLineChars = 76 - -type base64LineWrap struct { - writer io.Writer - numLineChars int -} - -func (e *base64LineWrap) Write(p []byte) (n int, err error) { - n = 0 - // while we have more chars than are allowed - for len(p)+e.numLineChars > maxLineChars { - numCharsToWrite := maxLineChars - e.numLineChars - // write the chars we can - /* #nosec */ - //nolint #nosec - _, _ = e.writer.Write(p[:numCharsToWrite]) - // write a line break - /* #nosec */ - //nolint #nosec - _, _ = e.writer.Write([]byte("\r\n")) - // reset the line count - e.numLineChars = 0 - // remove the chars that have been written - p = p[numCharsToWrite:] - // set the num of chars written - n += numCharsToWrite - } - - // write what is left - /* #nosec */ - //nolint #nosec - _, _ = e.writer.Write(p) - e.numLineChars += len(p) - n += len(p) - - return -} diff --git a/mail/contentType.go b/mail/contentType.go index 3bd45a5..5f1efe5 100644 --- a/mail/contentType.go +++ b/mail/contentType.go @@ -28,9 +28,9 @@ package mail type ContentType uint8 const ( - // TextPlain sets body type to text/plain in message body + // ContentPlainText sets body type to text/plain in message body. ContentPlainText ContentType = iota - // TextHTML sets body type to text/html in message body + // ContentHTML sets body type to text/html in message body. ContentHTML ) @@ -44,14 +44,3 @@ func (c ContentType) String() string { return ContentPlainText.String() } - -func (c ContentType) getContentType() string { - switch c { - case ContentPlainText: - return "text/plain" - case ContentHTML: - return "text/html" - } - - return ContentPlainText.getContentType() -} diff --git a/mail/encoder.go b/mail/encoder.go deleted file mode 100644 index 4ace3a9..0000000 --- a/mail/encoder.go +++ /dev/null @@ -1,200 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020 Nicolas JUHEL - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package mail - -import ( - "bufio" - "bytes" - "fmt" - "io" - "strings" - "unicode/utf8" -) - -type encoder struct { - w *bufio.Writer - charset string - usedChars int -} - -// newEncoder returns a new mime header encoder that writes to w. The c -// parameter specifies the name of the character set of the text that will be -// encoded. The u parameter indicates how many characters have been used -// already. -func newEncoder(w io.Writer, c string, u int) *encoder { - return &encoder{bufio.NewWriter(w), strings.ToUpper(c), u} -} - -// encode encodes p using the "Q" encoding and writes it to the underlying -// io.Writer. It limits line length to 75 characters. -func (e *encoder) encode(p []byte) (n int, err error) { - var output bytes.Buffer - allPrintable := true - - // some lines we encode end in " - //maxLineLength := 75 - 1 - maxLineLength := 76 - - // prevent header injection - p = e.secureHeader(p) - - // check to see if we have all printable characters - for _, c := range p { - if !e.isVchar(c) && !e.isWSP(c) { - allPrintable = false - break - } - } - - // all characters are printable. just do line folding - if allPrintable { - text := string(p) - words := strings.Split(text, " ") - - lineBuffer := "" - firstWord := true - - // split the line where necessary - for _, word := range words { - - newWord := "" - if !firstWord { - newWord += " " - } - newWord += word - - // check line length - if (e.usedChars+len(lineBuffer)+len(newWord) /*+len(" ")+len(word)*/) > maxLineLength && (lineBuffer != "" || e.usedChars != 0) { - output.WriteString(lineBuffer + "\r\n") - - // first word on newline needs a space in front - if !firstWord { - lineBuffer = "" - } else { - lineBuffer = " " - } - - // reset since not on the first line anymore - e.usedChars = 0 - } - lineBuffer += newWord /*word*/ - - firstWord = false - } - - output.WriteString(lineBuffer) - - } else { - firstLine := true - - // A single encoded word can not be longer than 75 characters - if e.usedChars == 0 { - maxLineLength = 75 - } - - wordBegin := "=?" + e.charset + "?Q?" - wordEnd := "?=" - - lineBuffer := wordBegin - - for i := 0; i < len(p); { - // encode the character - encodedChar, runeLength := e.encodeChar(p, i) - - // Check line length - if len(lineBuffer)+e.usedChars+len(encodedChar) > (maxLineLength - len(wordEnd)) { - output.WriteString(lineBuffer + wordEnd + "\r\n") - lineBuffer = " " + wordBegin - firstLine = false - } - - lineBuffer += encodedChar - - i = i + runeLength - - // reset since not on the first line anymore - if !firstLine { - e.usedChars = 0 - maxLineLength = 76 - } - } - - output.WriteString(lineBuffer + wordEnd) - } - - _, _ = e.w.Write(output.Bytes()) - _ = e.w.Flush() - n = output.Len() - - return n, nil -} - -// encode takes a string and position in that string and encodes one utf-8 -// character. It then returns the encoded string and number of runes in the -// character. -func (e *encoder) encodeChar(text []byte, i int) (encodedString string, runeLength int) { - started := false - - for ; i < len(text) && (!utf8.RuneStart(text[i]) || !started); i++ { - switch c := text[i]; { - case c == ' ': - encodedString += "_" - case e.isVchar(c) && c != '=' && c != '?' && c != '_': - encodedString += string(c) - default: - encodedString += fmt.Sprintf("=%02X", c) - } - - runeLength++ - - started = true - } - - return -} - -// secureHeader removes all unnecessary values to prevent -// header injection -func (e *encoder) secureHeader(text []byte) []byte { - secureValue := strings.TrimSpace(string(text)) - secureValue = strings.Replace(secureValue, "\r", "", -1) - secureValue = strings.Replace(secureValue, "\n", "", -1) - secureValue = strings.Replace(secureValue, "\t", "", -1) - - return []byte(secureValue) -} - -// isVchar returns true if c is an RFC 5322 VCHAR character. -func (e *encoder) isVchar(c byte) bool { - // Visible (printing) characters. - return '!' <= c && c <= '~' -} - -// isWSP returns true if c is a WSP (white space). -// WSP is a space or horizontal tab (RFC5234 Appendix B). -func (e *encoder) isWSP(c byte) bool { - return c == ' ' || c == '\t' -} diff --git a/mail/encoding.go b/mail/encoding.go index de19e56..7a377db 100644 --- a/mail/encoding.go +++ b/mail/encoding.go @@ -28,16 +28,16 @@ package mail type Encoding uint8 const ( - // EncodingNone turns off encoding on the message body + // EncodingNone turns off encoding on the message body. EncodingNone Encoding = iota - // EncodingBinary is equal to EncodingNone, but string is set to binrary instead of none + // EncodingBinary is equal to EncodingNone, but string is set to binary instead of none. EncodingBinary - // EncodingBase64 sets the message body encoding to base64 + // EncodingBase64 sets the message body encoding to base64. EncodingBase64 - // EncodingQuotedPrintable sets the message body encoding to quoted-printable + // EncodingQuotedPrintable sets the message body encoding to quoted-printable. EncodingQuotedPrintable ) @@ -54,16 +54,3 @@ func (e Encoding) String() string { } return EncodingNone.String() } - -func (e Encoding) getEncoding() string { - switch e { - case EncodingNone, EncodingBinary: - return "binary" - case EncodingBase64: - return "base64" - case EncodingQuotedPrintable: - return "quoted-printable" - } - - return EncodingNone.getEncoding() -} diff --git a/mail/mail.go b/mail/mail.go index 5caf6a6..b427abf 100644 --- a/mail/mail.go +++ b/mail/mail.go @@ -36,7 +36,7 @@ import ( ) const ( - MailDateTimeLayout = time.RFC1123Z + DateTimeLayout = time.RFC1123Z headerMimeVersion = "MIME-Version" headerDate = "Date" @@ -44,17 +44,16 @@ const ( ) type mail struct { - headers textproto.MIMEHeader - charset string - encoding Encoding date time.Time + attach []File + inline []File + body []Body + charset string subject string + headers textproto.MIMEHeader + address *email + encoding Encoding priority Priority - - address *email - attach []File - inline []File - body []Body } func (m *mail) Email() Email { @@ -112,7 +111,7 @@ func (m *mail) SetDateString(layout, datetime string) liberr.Error { } func (m *mail) GetDateString() string { - return m.date.Format(MailDateTimeLayout) + return m.date.Format(DateTimeLayout) } func (m *mail) AddHeader(key string, values ...string) { @@ -182,7 +181,7 @@ func (m *mail) GetHeaders() textproto.MIMEHeader { h = m.addHeader(h, key, values...) }) - for k, _ := range m.headers { + for k := range m.headers { h = m.addHeader(h, k, m.headers.Values(k)...) } diff --git a/mail/priority.go b/mail/priority.go index 5882807..6f3396b 100644 --- a/mail/priority.go +++ b/mail/priority.go @@ -34,11 +34,11 @@ const ( type Priority uint8 const ( - // PriorityNormal sets the email priority to normal + // PriorityNormal sets the email priority to normal. PriorityNormal Priority = iota - // PriorityLow sets the email priority to Low + // PriorityLow sets the email priority to Low. PriorityLow - // PriorityHigh sets the email priority to High + // PriorityHigh sets the email priority to High. PriorityHigh ) @@ -71,9 +71,9 @@ func (p Priority) headerPriority() string { func (p Priority) headerImportance() string { switch p { case PriorityLow: - return "Low" + return PriorityLow.String() case PriorityHigh: - return "High" + return PriorityHigh.String() case PriorityNormal: return "" } @@ -84,9 +84,9 @@ func (p Priority) headerImportance() string { func (p Priority) headerMSMailPriority() string { switch p { case PriorityLow: - return "Low" + return PriorityLow.String() case PriorityHigh: - return "High" + return PriorityHigh.String() case PriorityNormal: return "" } diff --git a/mail/sender.go b/mail/sender.go index 57cfe71..4b144d1 100644 --- a/mail/sender.go +++ b/mail/sender.go @@ -31,29 +31,32 @@ import ( "fmt" "io" - "github.com/nabbar/golib/smtp" - - "github.com/nabbar/golib/ioutils" - liberr "github.com/nabbar/golib/errors" + libiot "github.com/nabbar/golib/ioutils" + libsmtp "github.com/nabbar/golib/smtp" simple "github.com/xhit/go-simple-mail" ) +const ( + _MinSizeAddr = 4 +) + type Sender interface { Close() error - Send(ctx context.Context, cli smtp.SMTP) liberr.Error - SendClose(ctx context.Context, cli smtp.SMTP) liberr.Error + Send(ctx context.Context, cli libsmtp.SMTP) liberr.Error + SendClose(ctx context.Context, cli libsmtp.SMTP) liberr.Error } type sender struct { - data ioutils.FileProgress + data libiot.FileProgress from string rcpt []string } +//nolint #gocognit func (m *mail) Sender() (snd Sender, err liberr.Error) { e := simple.NewMSG() - f := make([]ioutils.FileProgress, 0) + f := make([]libiot.FileProgress, 0) switch m.GetPriority() { case PriorityHigh: @@ -142,7 +145,7 @@ func (m *mail) Sender() (snd Sender, err liberr.Error) { if len(m.attach) > 0 { for _, i := range m.attach { - if t, er := ioutils.NewFileProgressTemp(); er != nil { + if t, er := libiot.NewFileProgressTemp(); er != nil { return nil, er } else if _, er := t.ReadFrom(i.data); er != nil { return nil, ErrorIORead.ErrorParent(er) @@ -156,7 +159,7 @@ func (m *mail) Sender() (snd Sender, err liberr.Error) { if len(m.inline) > 0 { for _, i := range m.inline { - if t, er := ioutils.NewFileProgressTemp(); er != nil { + if t, er := libiot.NewFileProgressTemp(); er != nil { return nil, er } else if _, er := t.ReadFrom(i.data); er != nil { return nil, ErrorIORead.ErrorParent(er) @@ -206,7 +209,7 @@ func (m *mail) Sender() (snd Sender, err liberr.Error) { s.rcpt = append(s.rcpt, m.Email().GetRecipients(RecipientCC)...) s.rcpt = append(s.rcpt, m.Email().GetRecipients(RecipientBCC)...) - if tmp, err := ioutils.NewFileProgressTemp(); err != nil { + if tmp, err := libiot.NewFileProgressTemp(); err != nil { return nil, err } else if _, er := tmp.WriteString(e.GetMessage()); er != nil { return nil, ErrorIOWrite.ErrorParent(er) @@ -222,7 +225,7 @@ func (m *mail) Sender() (snd Sender, err liberr.Error) { return } -func (s *sender) SendClose(ctx context.Context, cli smtp.SMTP) liberr.Error { +func (s *sender) SendClose(ctx context.Context, cli libsmtp.SMTP) liberr.Error { defer func() { _ = s.Close() }() @@ -234,14 +237,16 @@ func (s *sender) SendClose(ctx context.Context, cli smtp.SMTP) liberr.Error { return nil } -func (s *sender) Send(ctx context.Context, cli smtp.SMTP) liberr.Error { +func (s *sender) Send(ctx context.Context, cli libsmtp.SMTP) liberr.Error { if e := cli.Check(ctx); e != nil { return ErrorSmtpClient.ErrorParent(e) } - if len(s.from) < 4 { + if len(s.from) < _MinSizeAddr { + //nolint #goerr113 return ErrorParamsEmpty.ErrorParent(fmt.Errorf("parameters 'from' is not valid")) - } else if len(s.rcpt) < 1 || len(s.rcpt[0]) < 4 { + } else if len(s.rcpt) < 1 || len(s.rcpt[0]) < _MinSizeAddr { + //nolint #goerr113 return ErrorParamsEmpty.ErrorParent(fmt.Errorf("parameters 'receipient' is not valid")) } diff --git a/mail/tools.go b/mail/tools.go deleted file mode 100644 index 20ddca6..0000000 --- a/mail/tools.go +++ /dev/null @@ -1,83 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020 Nicolas JUHEL - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package mail - -import ( - "bytes" - "encoding/base64" - "mime/quotedprintable" - "strings" -) - -// base64Encode base64 encodes the provided text with line wrapping -func base64Encode(text []byte) []byte { - // create buffer - buf := new(bytes.Buffer) - - // create base64 encoder that linewraps - encoder := base64.NewEncoder(base64.StdEncoding, &base64LineWrap{writer: buf}) - - // write the encoded text to buf - /* #nosec */ - //nolint #nosec - _, _ = encoder.Write(text) - _ = encoder.Close() - - return buf.Bytes() -} - -// qpEncode uses the quoted-printable encoding to encode the provided text -func qpEncode(text []byte) []byte { - // create buffer - buf := new(bytes.Buffer) - - encoder := quotedprintable.NewWriter(buf) - - /* #nosec */ - //nolint #nosec - _, _ = encoder.Write(text) - _ = encoder.Close() - - return buf.Bytes() -} - -func encodeHeader(text string, charset string, usedChars int) string { - // create buffer - buf := new(bytes.Buffer) - - // encode - encoder := newEncoder(buf, charset, usedChars) - /* #nosec */ - //nolint #nosec - _, _ = encoder.encode([]byte(text)) - - return buf.String() -} - -func escapeQuotes(s string) string { - quoteEscaper := strings.NewReplacer("\\", "\\\\", `"`, "\\\"") - return quoteEscaper.Replace(s) -} diff --git a/mailer/email.go b/mailer/email.go index bed8d0d..5391030 100644 --- a/mailer/email.go +++ b/mailer/email.go @@ -28,10 +28,10 @@ package mailer import "github.com/matcornic/hermes/v2" type email struct { - t Themes - d TextDirection p hermes.Product b *hermes.Body + t Themes + d TextDirection c bool } diff --git a/mailer/render.go b/mailer/render.go index c045483..1739d39 100644 --- a/mailer/render.go +++ b/mailer/render.go @@ -33,27 +33,14 @@ import ( ) func (e *email) GenerateHTML() (*bytes.Buffer, liberr.Error) { - var buf = bytes.NewBuffer(make([]byte, 0)) - - h := hermes.Hermes{ - Theme: e.t.getHermes(), - TextDirection: e.d.getDirection(), - Product: e.p, - DisableCSSInlining: e.c, - } - - if p, e := h.GenerateHTML(hermes.Email{ - Body: *e.b, - }); e != nil { - return nil, ErrorMailerHtml.ErrorParent(e) - } else { - buf.WriteString(p) - } - - return buf, nil + return e.generated(e.genHtml) } func (e *email) GeneratePlainText() (*bytes.Buffer, liberr.Error) { + return e.generated(e.genText) +} + +func (e email) generated(f func(h hermes.Hermes, m hermes.Email) (string, error)) (*bytes.Buffer, liberr.Error) { var buf = bytes.NewBuffer(make([]byte, 0)) h := hermes.Hermes{ @@ -63,7 +50,7 @@ func (e *email) GeneratePlainText() (*bytes.Buffer, liberr.Error) { DisableCSSInlining: e.c, } - if p, e := h.GeneratePlainText(hermes.Email{ + if p, e := f(h, hermes.Email{ Body: *e.b, }); e != nil { return nil, ErrorMailerText.ErrorParent(e) @@ -73,3 +60,11 @@ func (e *email) GeneratePlainText() (*bytes.Buffer, liberr.Error) { return buf, nil } + +func (e *email) genText(h hermes.Hermes, m hermes.Email) (string, error) { + return h.GeneratePlainText(m) +} + +func (e *email) genHtml(h hermes.Hermes, m hermes.Email) (string, error) { + return h.GenerateHTML(m) +} diff --git a/nutsdb/configDb.go b/nutsdb/configDb.go index cc44f4d..23c2196 100644 --- a/nutsdb/configDb.go +++ b/nutsdb/configDb.go @@ -66,6 +66,7 @@ func (o NutsDBOptions) GetNutsDBOptions(dataDir string) nutsdb.Options { } else { d.Dir = dataDir + //nolint #exhaustive switch o.RWMode { case nutsdb.MMap: d.RWMode = nutsdb.MMap @@ -73,6 +74,7 @@ func (o NutsDBOptions) GetNutsDBOptions(dataDir string) nutsdb.Options { d.RWMode = nutsdb.FileIO } + //nolint #exhaustive switch o.StartFileLoadingMode { case nutsdb.MMap: d.StartFileLoadingMode = nutsdb.MMap @@ -81,6 +83,7 @@ func (o NutsDBOptions) GetNutsDBOptions(dataDir string) nutsdb.Options { } } + //nolint #exhaustive switch o.EntryIdxMode { case nutsdb.HintKeyAndRAMIdxMode: d.EntryIdxMode = nutsdb.HintKeyAndRAMIdxMode diff --git a/nutsdb/snap.go b/nutsdb/snap.go index bdd3521..e2545ee 100644 --- a/nutsdb/snap.go +++ b/nutsdb/snap.go @@ -104,7 +104,7 @@ func (s *snap) Save(opt Options, writer io.Writer) liberr.Error { _ = t.Close() }() - if _, e = archive.CreateArchive(archive.ArchiveTypeTarGzip, t, s.path); e != nil { + if _, e = archive.CreateArchive(archive.TypeTarGzip, t, s.path); e != nil { return ErrorFolderArchive.Error(e) } diff --git a/router/register.go b/router/register.go index df1d7b3..a482072 100644 --- a/router/register.go +++ b/router/register.go @@ -29,12 +29,30 @@ import ( "github.com/gin-gonic/gin" ) -const EMPTY_GROUP = "" +const EmptyHandlerGroup = "" var ( - defaultRouters = NewRouterList() + defaultRouters = NewRouterList(DefaultGinInit) ) +func DefaultGinInit() *gin.Engine { + engine := gin.New() + engine.Use(gin.Logger(), gin.Recovery()) + + return engine +} + +func DefaultGinWithTrustyProxy(trustyProxy []string) *gin.Engine { + engine := gin.New() + engine.Use(gin.Logger(), gin.Recovery()) + + if len(trustyProxy) > 0 { + engine.TrustedProxies = trustyProxy + } + + return engine +} + type routerItem struct { method string relative string @@ -42,6 +60,7 @@ type routerItem struct { } type routerList struct { + init func() *gin.Engine list map[string][]routerItem } @@ -52,6 +71,7 @@ type RouterList interface { Register(method string, relativePath string, router ...gin.HandlerFunc) RegisterInGroup(group, method string, relativePath string, router ...gin.HandlerFunc) Handler(engine *gin.Engine) + Engine() *gin.Engine } func RoutersRegister(method string, relativePath string, router ...gin.HandlerFunc) { @@ -66,15 +86,16 @@ func RoutersHandler(engine *gin.Engine) { defaultRouters.Handler(engine) } -func NewRouterList() RouterList { +func NewRouterList(initGin func() *gin.Engine) RouterList { return &routerList{ + init: initGin, list: make(map[string][]routerItem), } } func (l routerList) Handler(engine *gin.Engine) { for grpRoute, grpList := range l.list { - if grpRoute == EMPTY_GROUP { + if grpRoute == EmptyHandlerGroup { for _, r := range grpList { engine.Handle(r.method, r.relative, r.router...) } @@ -89,7 +110,7 @@ func (l routerList) Handler(engine *gin.Engine) { func (l *routerList) RegisterInGroup(group, method string, relativePath string, router ...gin.HandlerFunc) { if group == "" { - group = EMPTY_GROUP + group = EmptyHandlerGroup } if _, ok := l.list[group]; !ok { @@ -106,3 +127,11 @@ func (l *routerList) RegisterInGroup(group, method string, relativePath string, func (l *routerList) Register(method string, relativePath string, router ...gin.HandlerFunc) { l.RegisterInGroup("", method, relativePath, router...) } + +func (l routerList) Engine() *gin.Engine { + if l.init != nil { + return l.init() + } else { + return DefaultGinInit() + } +} diff --git a/router/router.go b/router/router.go index 9c2783a..fdf0fb4 100644 --- a/router/router.go +++ b/router/router.go @@ -44,14 +44,13 @@ func SetGinHandler(fct func(c *gin.Context)) gin.HandlerFunc { } func Handler(routerList RouterList) http.Handler { - engine := gin.New() - engine.Use(gin.Logger(), gin.Recovery()) + e := routerList.Engine() if routerList == nil { - RoutersHandler(engine) + RoutersHandler(e) } else { - routerList.Handler(engine) + routerList.Handler(e) } - return engine + return e } diff --git a/smtp/dial.go b/smtp/dial.go index 3cacbf0..327f94f 100644 --- a/smtp/dial.go +++ b/smtp/dial.go @@ -70,17 +70,6 @@ func (s *smtpClient) dial(ctx context.Context, addr string) (con net.Conn, err l return } -func (s *smtpClient) dialStarttls(ctx context.Context, addr string, tlsConfig *tls.Config) (con net.Conn, err liberr.Error) { - defer func() { - if err != nil && con != nil { - _ = con.Close() - } - }() - - con, err = s.dial(ctx, addr) - return -} - func (s *smtpClient) client(ctx context.Context, addr string, tlsConfig *tls.Config) (cli *smtp.Client, con net.Conn, err liberr.Error) { var e error diff --git a/smtp/model.go b/smtp/model.go index 819cb7e..3f532f3 100644 --- a/smtp/model.go +++ b/smtp/model.go @@ -120,7 +120,7 @@ func (s *smtpClient) Client(ctx context.Context) (*smtp.Client, errors.Error) { return s.cli, nil } -// validateLine checks to see if a line has CR or LF as per RFC 5321 +// validateLine checks to see if a line has CR or LF as per RFC 5321. func (s smtpClient) validateLine(line string) errors.Error { if strings.ContainsAny(line, "\n\r") { return ErrorSMTPLineCRLF.Error(nil) diff --git a/status/interface.go b/status/interface.go index 03a7112..e0ea217 100644 --- a/status/interface.go +++ b/status/interface.go @@ -29,10 +29,9 @@ import ( "net/http" "sync/atomic" - "github.com/nabbar/golib/version" - "github.com/gin-gonic/gin" - "github.com/nabbar/golib/router" + librtr "github.com/nabbar/golib/router" + libver "github.com/nabbar/golib/version" ) const statusOK = "OK" @@ -82,8 +81,8 @@ type RouteStatus interface { HttpStatusCode(codeOk, codeKO, codeWarning int) Get(c *gin.Context) - Register(prefix string, register router.RegisterRouter) - RegisterGroup(group, prefix string, register router.RegisterRouterInGroup) + Register(prefix string, register librtr.RegisterRouter) + RegisterGroup(group, prefix string, register librtr.RegisterRouterInGroup) ComponentNew(key string, cpt Component) ComponentDel(key string) @@ -102,10 +101,10 @@ func New(Name string, Release string, Hash string, msgOk string, msgKo string, m cKO: http.StatusServiceUnavailable, mWM: msgWarm, cWM: http.StatusOK, - c: make(map[string]*atomic.Value, 0), + c: make(map[string]*atomic.Value), } } -func NewVersion(version version.Version, msgOk string, msgKO string, msgWarm string) RouteStatus { +func NewVersion(version libver.Version, msgOk string, msgKO string, msgWarm string) RouteStatus { return New(version.GetPackage(), version.GetRelease(), version.GetBuild(), msgOk, msgKO, msgWarm) } diff --git a/status/model.go b/status/model.go index 1c16320..6661bf0 100644 --- a/status/model.go +++ b/status/model.go @@ -31,12 +31,11 @@ import ( "strings" "sync/atomic" + "github.com/gin-gonic/gin" liberr "github.com/nabbar/golib/errors" liblog "github.com/nabbar/golib/logger" - - "github.com/gin-gonic/gin" - "github.com/nabbar/golib/router" - "github.com/nabbar/golib/semaphore" + librtr "github.com/nabbar/golib/router" + libsem "github.com/nabbar/golib/semaphore" ) type rtrStatus struct { @@ -74,7 +73,7 @@ func (r *rtrStatus) cleanPrefix(prefix string) string { return path.Clean(strings.TrimRight(path.Join("/", prefix), "/")) } -func (r *rtrStatus) Register(prefix string, register router.RegisterRouter) { +func (r *rtrStatus) Register(prefix string, register librtr.RegisterRouter) { prefix = r.cleanPrefix(prefix) var m = r.m @@ -86,7 +85,7 @@ func (r *rtrStatus) Register(prefix string, register router.RegisterRouter) { } } -func (r *rtrStatus) RegisterGroup(group, prefix string, register router.RegisterRouterInGroup) { +func (r *rtrStatus) RegisterGroup(group, prefix string, register librtr.RegisterRouterInGroup) { prefix = r.cleanPrefix(prefix) var m = r.m @@ -107,7 +106,7 @@ func (r *rtrStatus) Get(x *gin.Context) { key string err liberr.Error rsp *Response - sem semaphore.Sem + sem libsem.Sem ) defer func() { @@ -130,7 +129,7 @@ func (r *rtrStatus) Get(x *gin.Context) { Components: make([]CptResponse, 0), } - sem = semaphore.NewSemaphoreWithContext(x, 0) + sem = libsem.NewSemaphoreWithContext(x, 0) for key, atm = range r.c { if atm == nil { @@ -182,7 +181,7 @@ func (r *rtrStatus) Get(x *gin.Context) { func (r *rtrStatus) ComponentNew(key string, cpt Component) { if len(r.c) < 1 { - r.c = make(map[string]*atomic.Value, 0) + r.c = make(map[string]*atomic.Value) } if _, ok := r.c[key]; !ok { diff --git a/test/test-nutsdb/main.go b/test/test-nutsdb/main.go index 997043f..b7bebc0 100644 --- a/test/test-nutsdb/main.go +++ b/test/test-nutsdb/main.go @@ -75,6 +75,8 @@ func main() { } } + //nolint #gosec + /* #nosec */ if file, err := os.OpenFile(LoggerFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666); err != nil { panic(err) } else {