From 90953f9b75cb0d06a4dfe50af48a22a35bff9567 Mon Sep 17 00:00:00 2001 From: Adam Borbas Date: Thu, 23 Jul 2020 12:16:42 +0200 Subject: [PATCH] Direct gradle output to log file if debug flag used (#76) * Dependency update * Not generic working * Direct gradle output to log file if debug flag used * Left outs * typos * Fix ci * Fix CI Co-authored-by: Tamas Papik --- Gopkg.lock | 98 +- bitrise.yml | 32 + main.go | 61 +- step.yml | 3 + .../commandhelper/commandhelper.go | 61 + .../bitrise-io/go-steputils/output/output.go | 104 + .../bitrise-io/go-utils/pkcs12/README.md | 35 + .../bitrise-io/go-utils/pkcs12/bmp-string.go | 50 + .../bitrise-io/go-utils/pkcs12/crypto.go | 173 + .../bitrise-io/go-utils/pkcs12/errors.go | 23 + .../go-utils/pkcs12/internal/rc2/rc2.go | 271 + .../bitrise-io/go-utils/pkcs12/mac.go | 60 + .../bitrise-io/go-utils/pkcs12/pbkdf.go | 170 + .../bitrise-io/go-utils/pkcs12/pkcs12.go | 556 ++ .../bitrise-io/go-utils/pkcs12/safebags.go | 99 + .../go-utils/sliceutil/sliceutil.go | 46 + .../go-utils/stringutil/stringutil.go | 104 + .../bitrise-io/go-utils/stringutil/text.go | 49 + .../bitrise-io/go-utils/ziputil/ziputil.go | 71 + .../go-xcode/certificateutil/filter.go | 58 + .../go-xcode/certificateutil/info_model.go | 119 + .../go-xcode/certificateutil/test_util.go | 80 + .../go-xcode/certificateutil/util.go | 179 + .../exportoptions/appstore_options.go | 80 + .../go-xcode/exportoptions/exportoptions.go | 46 + .../exportoptions/non_appstore_options.go | 93 + .../go-xcode/exportoptions/properties.go | 159 + .../bitrise-io/go-xcode/models/models.go | 8 + .../go-xcode/plistutil/plistutil.go | 230 + .../plistutil/plistutil_test_file_content.go | 331 + .../go-xcode/profileutil/capabilities.go | 63 + .../go-xcode/profileutil/info_model.go | 227 + .../go-xcode/profileutil/plist_data.go | 175 + .../bitrise-io/go-xcode/profileutil/util.go | 108 + .../bitrise-io/go-xcode/utility/glob.go | 13 + .../bitrise-io/go-xcode/utility/path.go | 103 + .../bitrise-io/go-xcode/utility/utility.go | 51 + .../bitrise-io/xcode-project/.gitignore | 5 + .../bitrise-io/xcode-project/README.md | 1 + .../bitrise-io/xcode-project/bitrise.yml | 31 + .../bitrise-io/xcode-project/pretty/pretty.go | 16 + .../bitrise-io/xcode-project/project.go | 27 + .../xcode-project/serialized/error.go | 54 + .../xcode-project/serialized/serialized.go | 77 + .../xcode-project/xcodebuild/xcodebuild.go | 74 + .../xcode-project/xcodeproj/appiconset.go | 170 + .../xcode-project/xcodeproj/attributes.go | 61 + .../xcodeproj/build_configuration.go | 33 + .../xcodeproj/configuration_list.go | 89 + .../xcode-project/xcodeproj/plist.go | 53 + .../xcodeproj/product_reference.go | 24 + .../xcode-project/xcodeproj/proj.go | 96 + .../xcodeproj/resources_build_phase.go | 250 + .../xcode-project/xcodeproj/target.go | 181 + .../xcodeproj/target_dependency.go | 31 + .../xcode-project/xcodeproj/xcodeproj.go | 490 ++ .../xcode-project/xcscheme/errors.go | 23 + .../bitrise-io/xcode-project/xcscheme/util.go | 36 + .../xcode-project/xcscheme/xcscheme.go | 110 + .../xcode-project/xcworkspace/file_ref.go | 61 + .../xcode-project/xcworkspace/group.go | 54 + .../xcode-project/xcworkspace/xcworkspace.go | 154 + .../steps-xcode-archive/LICENSE | 22 + .../utils/code_sign_mapping.go | 118 + .../steps-xcode-archive/utils/entitlements.go | 52 + .../steps-xcode-archive/utils/export.go | 78 + .../steps-xcode-archive/utils/platform.go | 138 + .../steps-xcode-archive/utils/profile.go | 57 + .../utils/sort_profiles.go | 21 + vendor/github.com/fullsailor/pkcs7/.gitignore | 24 + .../github.com/fullsailor/pkcs7/.travis.yml | 7 + vendor/github.com/fullsailor/pkcs7/LICENSE | 22 + vendor/github.com/fullsailor/pkcs7/README.md | 8 + vendor/github.com/fullsailor/pkcs7/ber.go | 248 + vendor/github.com/fullsailor/pkcs7/pkcs7.go | 962 ++ vendor/github.com/fullsailor/pkcs7/x509.go | 133 + vendor/github.com/pkg/errors/.gitignore | 24 + vendor/github.com/pkg/errors/.travis.yml | 15 + vendor/github.com/pkg/errors/LICENSE | 23 + vendor/github.com/pkg/errors/README.md | 52 + vendor/github.com/pkg/errors/appveyor.yml | 32 + vendor/github.com/pkg/errors/errors.go | 282 + vendor/github.com/pkg/errors/stack.go | 147 + vendor/golang.org/x/text/AUTHORS | 3 + vendor/golang.org/x/text/CONTRIBUTORS | 3 + vendor/golang.org/x/text/LICENSE | 27 + vendor/golang.org/x/text/PATENTS | 22 + vendor/golang.org/x/text/internal/gen/code.go | 375 + vendor/golang.org/x/text/internal/gen/gen.go | 348 + .../x/text/internal/triegen/compact.go | 58 + .../x/text/internal/triegen/print.go | 251 + .../x/text/internal/triegen/triegen.go | 494 ++ vendor/golang.org/x/text/internal/ucd/ucd.go | 371 + .../golang.org/x/text/transform/transform.go | 709 ++ vendor/golang.org/x/text/unicode/cldr/base.go | 105 + vendor/golang.org/x/text/unicode/cldr/cldr.go | 137 + .../golang.org/x/text/unicode/cldr/collate.go | 359 + .../golang.org/x/text/unicode/cldr/decode.go | 172 + .../golang.org/x/text/unicode/cldr/makexml.go | 400 + .../golang.org/x/text/unicode/cldr/resolve.go | 602 ++ .../golang.org/x/text/unicode/cldr/slice.go | 144 + vendor/golang.org/x/text/unicode/cldr/xml.go | 1494 ++++ .../x/text/unicode/norm/composition.go | 512 ++ .../x/text/unicode/norm/forminfo.go | 278 + .../golang.org/x/text/unicode/norm/input.go | 109 + vendor/golang.org/x/text/unicode/norm/iter.go | 458 + .../x/text/unicode/norm/maketables.go | 986 +++ .../x/text/unicode/norm/normalize.go | 609 ++ .../x/text/unicode/norm/readwriter.go | 125 + .../x/text/unicode/norm/tables10.0.0.go | 7657 ++++++++++++++++ .../x/text/unicode/norm/tables11.0.0.go | 7693 ++++++++++++++++ .../x/text/unicode/norm/tables12.0.0.go | 7710 +++++++++++++++++ .../x/text/unicode/norm/tables9.0.0.go | 7637 ++++++++++++++++ .../x/text/unicode/norm/transform.go | 88 + vendor/golang.org/x/text/unicode/norm/trie.go | 54 + .../golang.org/x/text/unicode/norm/triegen.go | 117 + vendor/howett.net/plist/.gitignore | 16 + vendor/howett.net/plist/.gitlab-ci.yml | 39 + vendor/howett.net/plist/LICENSE | 58 + vendor/howett.net/plist/README.md | 21 + vendor/howett.net/plist/bplist.go | 26 + vendor/howett.net/plist/bplist_generator.go | 303 + vendor/howett.net/plist/bplist_parser.go | 353 + vendor/howett.net/plist/decode.go | 119 + vendor/howett.net/plist/doc.go | 5 + vendor/howett.net/plist/encode.go | 126 + vendor/howett.net/plist/fuzz.go | 17 + vendor/howett.net/plist/go.mod | 9 + vendor/howett.net/plist/marshal.go | 186 + vendor/howett.net/plist/must.go | 50 + vendor/howett.net/plist/plist.go | 83 + vendor/howett.net/plist/plist_types.go | 172 + vendor/howett.net/plist/text_generator.go | 228 + vendor/howett.net/plist/text_parser.go | 580 ++ vendor/howett.net/plist/text_tables.go | 60 + vendor/howett.net/plist/typeinfo.go | 170 + vendor/howett.net/plist/unmarshal.go | 317 + vendor/howett.net/plist/util.go | 25 + vendor/howett.net/plist/xml_generator.go | 178 + vendor/howett.net/plist/xml_parser.go | 211 + vendor/howett.net/plist/zerocopy.go | 20 + vendor/howett.net/plist/zerocopy_appengine.go | 7 + 142 files changed, 52317 insertions(+), 24 deletions(-) create mode 100644 vendor/github.com/bitrise-io/go-steputils/commandhelper/commandhelper.go create mode 100644 vendor/github.com/bitrise-io/go-steputils/output/output.go create mode 100644 vendor/github.com/bitrise-io/go-utils/pkcs12/README.md create mode 100644 vendor/github.com/bitrise-io/go-utils/pkcs12/bmp-string.go create mode 100644 vendor/github.com/bitrise-io/go-utils/pkcs12/crypto.go create mode 100644 vendor/github.com/bitrise-io/go-utils/pkcs12/errors.go create mode 100644 vendor/github.com/bitrise-io/go-utils/pkcs12/internal/rc2/rc2.go create mode 100644 vendor/github.com/bitrise-io/go-utils/pkcs12/mac.go create mode 100644 vendor/github.com/bitrise-io/go-utils/pkcs12/pbkdf.go create mode 100644 vendor/github.com/bitrise-io/go-utils/pkcs12/pkcs12.go create mode 100644 vendor/github.com/bitrise-io/go-utils/pkcs12/safebags.go create mode 100644 vendor/github.com/bitrise-io/go-utils/sliceutil/sliceutil.go create mode 100644 vendor/github.com/bitrise-io/go-utils/stringutil/stringutil.go create mode 100644 vendor/github.com/bitrise-io/go-utils/stringutil/text.go create mode 100644 vendor/github.com/bitrise-io/go-utils/ziputil/ziputil.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/certificateutil/filter.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/certificateutil/info_model.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/certificateutil/test_util.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/certificateutil/util.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/exportoptions/appstore_options.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/exportoptions/exportoptions.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/exportoptions/non_appstore_options.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/exportoptions/properties.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/models/models.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/plistutil/plistutil.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/plistutil/plistutil_test_file_content.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/profileutil/capabilities.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/profileutil/info_model.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/profileutil/plist_data.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/profileutil/util.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/utility/glob.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/utility/path.go create mode 100644 vendor/github.com/bitrise-io/go-xcode/utility/utility.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/.gitignore create mode 100644 vendor/github.com/bitrise-io/xcode-project/README.md create mode 100644 vendor/github.com/bitrise-io/xcode-project/bitrise.yml create mode 100644 vendor/github.com/bitrise-io/xcode-project/pretty/pretty.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/project.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/serialized/error.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/serialized/serialized.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcodebuild/xcodebuild.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcodeproj/appiconset.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcodeproj/attributes.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcodeproj/build_configuration.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcodeproj/configuration_list.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcodeproj/plist.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcodeproj/product_reference.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcodeproj/proj.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcodeproj/resources_build_phase.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcodeproj/target.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcodeproj/target_dependency.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcodeproj/xcodeproj.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcscheme/errors.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcscheme/util.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcscheme/xcscheme.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcworkspace/file_ref.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcworkspace/group.go create mode 100644 vendor/github.com/bitrise-io/xcode-project/xcworkspace/xcworkspace.go create mode 100644 vendor/github.com/bitrise-steplib/steps-xcode-archive/LICENSE create mode 100644 vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/code_sign_mapping.go create mode 100644 vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/entitlements.go create mode 100644 vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/export.go create mode 100644 vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/platform.go create mode 100644 vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/profile.go create mode 100644 vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/sort_profiles.go create mode 100644 vendor/github.com/fullsailor/pkcs7/.gitignore create mode 100644 vendor/github.com/fullsailor/pkcs7/.travis.yml create mode 100644 vendor/github.com/fullsailor/pkcs7/LICENSE create mode 100644 vendor/github.com/fullsailor/pkcs7/README.md create mode 100644 vendor/github.com/fullsailor/pkcs7/ber.go create mode 100644 vendor/github.com/fullsailor/pkcs7/pkcs7.go create mode 100644 vendor/github.com/fullsailor/pkcs7/x509.go create mode 100644 vendor/github.com/pkg/errors/.gitignore create mode 100644 vendor/github.com/pkg/errors/.travis.yml create mode 100644 vendor/github.com/pkg/errors/LICENSE create mode 100644 vendor/github.com/pkg/errors/README.md create mode 100644 vendor/github.com/pkg/errors/appveyor.yml create mode 100644 vendor/github.com/pkg/errors/errors.go create mode 100644 vendor/github.com/pkg/errors/stack.go create mode 100644 vendor/golang.org/x/text/AUTHORS create mode 100644 vendor/golang.org/x/text/CONTRIBUTORS create mode 100644 vendor/golang.org/x/text/LICENSE create mode 100644 vendor/golang.org/x/text/PATENTS create mode 100644 vendor/golang.org/x/text/internal/gen/code.go create mode 100644 vendor/golang.org/x/text/internal/gen/gen.go create mode 100644 vendor/golang.org/x/text/internal/triegen/compact.go create mode 100644 vendor/golang.org/x/text/internal/triegen/print.go create mode 100644 vendor/golang.org/x/text/internal/triegen/triegen.go create mode 100644 vendor/golang.org/x/text/internal/ucd/ucd.go create mode 100644 vendor/golang.org/x/text/transform/transform.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/base.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/cldr.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/collate.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/decode.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/makexml.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/resolve.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/slice.go create mode 100644 vendor/golang.org/x/text/unicode/cldr/xml.go create mode 100644 vendor/golang.org/x/text/unicode/norm/composition.go create mode 100644 vendor/golang.org/x/text/unicode/norm/forminfo.go create mode 100644 vendor/golang.org/x/text/unicode/norm/input.go create mode 100644 vendor/golang.org/x/text/unicode/norm/iter.go create mode 100644 vendor/golang.org/x/text/unicode/norm/maketables.go create mode 100644 vendor/golang.org/x/text/unicode/norm/normalize.go create mode 100644 vendor/golang.org/x/text/unicode/norm/readwriter.go create mode 100644 vendor/golang.org/x/text/unicode/norm/tables10.0.0.go create mode 100644 vendor/golang.org/x/text/unicode/norm/tables11.0.0.go create mode 100644 vendor/golang.org/x/text/unicode/norm/tables12.0.0.go create mode 100644 vendor/golang.org/x/text/unicode/norm/tables9.0.0.go create mode 100644 vendor/golang.org/x/text/unicode/norm/transform.go create mode 100644 vendor/golang.org/x/text/unicode/norm/trie.go create mode 100644 vendor/golang.org/x/text/unicode/norm/triegen.go create mode 100644 vendor/howett.net/plist/.gitignore create mode 100644 vendor/howett.net/plist/.gitlab-ci.yml create mode 100644 vendor/howett.net/plist/LICENSE create mode 100644 vendor/howett.net/plist/README.md create mode 100644 vendor/howett.net/plist/bplist.go create mode 100644 vendor/howett.net/plist/bplist_generator.go create mode 100644 vendor/howett.net/plist/bplist_parser.go create mode 100644 vendor/howett.net/plist/decode.go create mode 100644 vendor/howett.net/plist/doc.go create mode 100644 vendor/howett.net/plist/encode.go create mode 100644 vendor/howett.net/plist/fuzz.go create mode 100644 vendor/howett.net/plist/go.mod create mode 100644 vendor/howett.net/plist/marshal.go create mode 100644 vendor/howett.net/plist/must.go create mode 100644 vendor/howett.net/plist/plist.go create mode 100644 vendor/howett.net/plist/plist_types.go create mode 100644 vendor/howett.net/plist/text_generator.go create mode 100644 vendor/howett.net/plist/text_parser.go create mode 100644 vendor/howett.net/plist/text_tables.go create mode 100644 vendor/howett.net/plist/typeinfo.go create mode 100644 vendor/howett.net/plist/unmarshal.go create mode 100644 vendor/howett.net/plist/util.go create mode 100644 vendor/howett.net/plist/xml_generator.go create mode 100644 vendor/howett.net/plist/xml_parser.go create mode 100644 vendor/howett.net/plist/zerocopy.go create mode 100644 vendor/howett.net/plist/zerocopy_appengine.go diff --git a/Gopkg.lock b/Gopkg.lock index 4302463..f285cd7 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -7,23 +7,25 @@ name = "github.com/bitrise-io/go-android" packages = ["cache"] pruneopts = "UT" - revision = "a28433b493561a6575fcec699f93f124d22150d1" + revision = "a765b1761418206bd0bda269bee472efc1a77c4d" [[projects]] branch = "master" - digest = "1:a6c4b93cbf1598f39f75f8eb10aad73caf515d130c945da2fb9eb96743b23dc7" + digest = "1:92f42a1574d0e63eb1bc668446b2be42978b7e5d144d9d7cc2d3d38875fb83f4" name = "github.com/bitrise-io/go-steputils" packages = [ "cache", + "commandhelper", + "output", "stepconf", "tools", ] pruneopts = "UT" - revision = "94490ca44ddb645764676a94988f6fc7e3187383" + revision = "6d5afa53c7b85b6135e382105116d245b5997ace" [[projects]] branch = "master" - digest = "1:e188bd4ff7f6418d9203ad4a49b23f18d0a7d1b96f4a30bbff6dfea9d75a5dc6" + digest = "1:18a613e27e8ea1f3a870915e2e04289a0eb54374b0378ec3e7def1e7a570d6c3" name = "github.com/bitrise-io/go-utils" packages = [ "colorstring", @@ -33,11 +35,63 @@ "log", "parseutil", "pathutil", + "pkcs12", + "pkcs12/internal/rc2", "pointers", "retry", + "sliceutil", + "stringutil", + "ziputil", ] pruneopts = "UT" - revision = "e212188d99b41373b4ff8b69e7b1f677bc427bde" + revision = "0c47c16813a4209321c9d0dba2e75867d6901ff2" + +[[projects]] + branch = "master" + digest = "1:25536a1adb812e180ba9cb88809206b2339b7e8a324f1e9bc41c1eb9a7c88cd1" + name = "github.com/bitrise-io/go-xcode" + packages = [ + "certificateutil", + "exportoptions", + "models", + "plistutil", + "profileutil", + "utility", + ] + pruneopts = "UT" + revision = "44336c3652b214617ec3f4f8cc82054edf76cbb9" + +[[projects]] + branch = "master" + digest = "1:5a0a434e00ef880e792870fdff66cc1125472494b7d6e14666738125c339a043" + name = "github.com/bitrise-io/xcode-project" + packages = [ + ".", + "pretty", + "serialized", + "xcodebuild", + "xcodeproj", + "xcscheme", + "xcworkspace", + ] + pruneopts = "UT" + revision = "9481b30b489b9eaf073c818fcf4817b5b3224285" + +[[projects]] + digest = "1:2e5190f947f18948a99839539a0db24d57f8f52b6a059317971e07f3bde4f876" + name = "github.com/bitrise-steplib/steps-xcode-archive" + packages = ["utils"] + pruneopts = "UT" + revision = "f9b8f6015ab17b8abf4bd9d829504dce349830ca" + version = "2.8.5" + +[[projects]] + branch = "master" + digest = "1:0bd05495dbd0567480e48eac9e6602d87875a8747b13c3c0fcb7c83c7c921776" + name = "github.com/fullsailor/pkcs7" + packages = ["."] + pruneopts = "UT" + revision = "d7302db945fa6ea264fb79d8e13e931ea514a602" [[projects]] branch = "master" @@ -47,6 +101,14 @@ pruneopts = "UT" revision = "95032a82bc518f77982ea72343cc1ade730072f0" +[[projects]] + digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b" + name = "github.com/pkg/errors" + packages = ["."] + pruneopts = "UT" + revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4" + version = "v0.8.1" + [[projects]] digest = "1:6baa565fe16f8657cf93469b2b8a6c61a277827734400d27e44d589547297279" name = "github.com/ryanuber/go-glob" @@ -55,16 +117,42 @@ revision = "51a8f68e6c24dc43f1e371749c89a267de4ebc53" version = "v1.0.0" +[[projects]] + digest = "1:a2c690dfee42801a26a0e88f69206feca8a3c2437fd476d84767468d7d7708e9" + name = "golang.org/x/text" + packages = [ + "internal/gen", + "internal/triegen", + "internal/ucd", + "transform", + "unicode/cldr", + "unicode/norm", + ] + pruneopts = "UT" + revision = "23ae387dee1f90d29a23c0e87ee0b46038fbed0e" + version = "v0.3.3" + +[[projects]] + branch = "master" + digest = "1:b5ead2120cd278858a83be14ef551f54295da350033dbc3f227fe0298b880256" + name = "howett.net/plist" + packages = ["."] + pruneopts = "UT" + revision = "3b63eb3a43b59c776909681ed1fcb412b47d3c9a" + [solve-meta] analyzer-name = "dep" analyzer-version = 1 input-imports = [ "github.com/bitrise-io/go-android/cache", + "github.com/bitrise-io/go-steputils/commandhelper", "github.com/bitrise-io/go-steputils/stepconf", + "github.com/bitrise-io/go-utils/colorstring", "github.com/bitrise-io/go-utils/command", "github.com/bitrise-io/go-utils/log", "github.com/bitrise-io/go-utils/pathutil", "github.com/bitrise-io/go-utils/retry", + "github.com/bitrise-steplib/steps-xcode-archive/utils", "github.com/kballard/go-shellquote", "github.com/ryanuber/go-glob", ] diff --git a/bitrise.yml b/bitrise.yml index 943b50b..af00b85 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -40,11 +40,13 @@ workflows: - GRADLE_FILE: ./build.gradle - GRADLE_TASK: assembleDebug - GRADLEW_PATH: ./gradlew + - GRADLE_OPTIONS: "--debug" - APK_FILE_EXCLUDE_FILTER: "*-unaligned.apk" - MAPPING_FILE_INCLUDE_FILTER: "*/release/mapping.txt" after_run: - _common - _check_output_apk + - _check_log_output test-aab: description: "Use app filter input" @@ -53,6 +55,7 @@ workflows: - GRADLE_FILE: ./build.gradle - GRADLE_TASK: bundle - GRADLEW_PATH: ./gradlew + - GRADLE_OPTIONS: "" - APP_FILE_EXCLUDE_FILTER: "*-unaligned.apk" - MAPPING_FILE_INCLUDE_FILTER: "*/release/mapping.txt" after_run: @@ -66,6 +69,7 @@ workflows: - GRADLE_FILE: ./build.gradle - GRADLE_TASK: assembleDebug - GRADLEW_PATH: gradlew + - GRADLE_OPTIONS: "" - APK_FILE_EXCLUDE_FILTER: "*-unaligned.apk" - MAPPING_FILE_INCLUDE_FILTER: "*/release/mapping.txt" after_run: @@ -78,6 +82,7 @@ workflows: - GRADLE_FILE: ./build.gradle - GRADLE_TASK: assembleDebug assembleRelease - GRADLEW_PATH: ./gradlew + - GRADLE_OPTIONS: "" - APK_FILE_EXCLUDE_FILTER: "*-unaligned.apk" - MAPPING_FILE_INCLUDE_FILTER: "*/release/mapping.txt" after_run: @@ -90,6 +95,7 @@ workflows: - GRADLE_FILE: ./src/build.gradle - GRADLE_TASK: assembleDebug - GRADLEW_PATH: ./src/gradlew + - GRADLE_OPTIONS: "" - APK_FILE_EXCLUDE_FILTER: "*-unaligned.apk" - MAPPING_FILE_INCLUDE_FILTER: "*/release/mapping.txt" after_run: @@ -102,6 +108,7 @@ workflows: - GRADLE_FILE: ./build.gradle - GRADLE_TASK: assembleDebug - GRADLEW_PATH: "" + - GRADLE_OPTIONS: "" - APK_FILE_EXCLUDE_FILTER: "*-unaligned.apk" - MAPPING_FILE_INCLUDE_FILTER: "*/release/mapping.txt" after_run: @@ -114,6 +121,7 @@ workflows: - GRADLE_FILE: "" - GRADLE_TASK: assembleDebug - GRADLEW_PATH: "./gradlew" + - GRADLE_OPTIONS: "" - APK_FILE_EXCLUDE_FILTER: "*-unaligned.apk" - MAPPING_FILE_INCLUDE_FILTER: "*/release/mapping.txt" after_run: @@ -126,6 +134,7 @@ workflows: - GRADLE_FILE: "" - GRADLE_TASK: assembleDebug - GRADLEW_PATH: "./gradlew" + - GRADLE_OPTIONS: "" - APK_FILE_EXCLUDE_FILTER: "*-unaligned.apk" - MAPPING_FILE_INCLUDE_FILTER: "*/release/mapping.txt" after_run: @@ -172,11 +181,15 @@ workflows: git remote add origin "${SAMPLE_APP_URL}" git fetch || exit 1 [[ -n "${COMMIT}" ]] && git checkout "${COMMIT}" || git checkout "${BRANCH}" + - install-missing-android-tools: + inputs: + - gradlew_path: $GRADLEW_PATH - path::./: inputs: - gradle_file: $GRADLE_FILE - gradle_task: $GRADLE_TASK - gradlew_path: $GRADLEW_PATH + - gradle_options: $GRADLE_OPTIONS - apk_file_include_filter: $APK_FILE_INCLUDE_FILTER - apk_file_exclude_filter: $APK_FILE_EXCLUDE_FILTER - mapping_file_include_filter: $MAPPING_FILE_INCLUDE_FILTER @@ -218,6 +231,25 @@ workflows: exit 1 fi echo "APK found at ${BITRISE_APK_PATH}" + + _check_log_output: + steps: + - script: + title: Check if log file exists and environment variable is set + inputs: + - content: |- + if [ -z "$BITRISE_GRADLE_RAW_RESULT_TEXT_PATH" ] ; then + echo "BITRISE_GRADLE_RAW_RESULT_TEXT_PATH env is empty" + exit 1 + fi + + if [ ! -f $BITRISE_GRADLE_RAW_RESULT_TEXT_PATH ]; then + echo "Log file not found at ${BITRISE_GRADLE_RAW_RESULT_TEXT_PATH}" + exit 1 + fi + echo "Log file found at ${BITRISE_GRADLE_RAW_RESULT_TEXT_PATH}" + + envman add --key BITRISE_GRADLE_RAW_RESULT_TEXT_PATH --value "" # ---------------------------------------------------------------- # --- Utility workflows diff --git a/main.go b/main.go index c0c0be3..4a3d2b4 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "time" "github.com/bitrise-io/go-android/cache" + "github.com/bitrise-io/go-steputils/commandhelper" "github.com/bitrise-io/go-steputils/stepconf" "github.com/bitrise-io/go-utils/command" "github.com/bitrise-io/go-utils/log" @@ -19,19 +20,23 @@ import ( "github.com/kballard/go-shellquote" ) -const failedToFindTargetWithHashString = `Failed to find target with hash string ` -const failedToFindBuildToolRevision = `Failed to find Build Tools revision ` -const failedToFindPlatformSDKWithPath = `Failed to find Platform SDK with path: ` -const couldNotHEAD = `Could not HEAD ` -const connectionTimedOut = `Connection timed out` -const couldNotRead = `Could not read ` -const couldNotGetResource = `Could not get resource ` -const couldNotGET = `Could not GET ` -const couldNotDownload = `Could not download ` -const receivedStatusCode503 = `Received status code 503 from server: Service Temporarily Unavailable` -const causeErrorInOpeningZipFile = `Cause: error in opening zip file.` -const failedToDownloadResource = `Failed to download resource` -const failedToDownloadSHA1ForResource = `Failed to download SHA1 for resource` +const ( + failedToFindTargetWithHashString = `Failed to find target with hash string ` + failedToFindBuildToolRevision = `Failed to find Build Tools revision ` + failedToFindPlatformSDKWithPath = `Failed to find Platform SDK with path: ` + couldNotHEAD = `Could not HEAD ` + connectionTimedOut = `Connection timed out` + couldNotRead = `Could not read ` + couldNotGetResource = `Could not get resource ` + couldNotGET = `Could not GET ` + couldNotDownload = `Could not download ` + receivedStatusCode503 = `Received status code 503 from server: Service Temporarily Unavailable` + causeErrorInOpeningZipFile = `Cause: error in opening zip file.` + failedToDownloadResource = `Failed to download resource` + failedToDownloadSHA1ForResource = `Failed to download SHA1 for resource` + bitriseGradleResultsTextEnvKey = "BITRISE_GRADLE_RAW_RESULT_TEXT_PATH" + rawGradleResultFileName = "raw-gradle-output.log" +) var automaticRetryReasonPatterns = []string{ failedToFindTargetWithHashString, @@ -98,7 +103,7 @@ func shouldRetry(outputToSearchIn string) (bool, string) { return isCouldNotFindInOutput(outputToSearchIn) } -func runGradleTask(gradleTool, buildFile, tasks, options string, isAutomaticRetryOnReason bool) error { +func runGradleTask(gradleTool, buildFile, tasks, options string, isAutomaticRetryOnReason bool, destDir string) error { optionSlice, err := shellquote.Split(options) if err != nil { return err @@ -123,13 +128,21 @@ func runGradleTask(gradleTool, buildFile, tasks, options string, isAutomaticRetr outWriter := io.MultiWriter(os.Stdout, &outBuffer) cmd := command.New(cmdSlice[0], cmdSlice[1:]...) - cmd.SetStdout(outWriter) - cmd.SetStderr(outWriter) - if err := cmd.Run(); err != nil { + + if shouldSaveOutputToLogFile(optionSlice) { + rawOutputLogPath := filepath.Join(destDir, rawGradleResultFileName) + err = commandhelper.RunAndExportOutput(*cmd, rawOutputLogPath, bitriseGradleResultsTextEnvKey, 20) + } else { + cmd.SetStdout(outWriter) + cmd.SetStderr(outWriter) + err = cmd.Run() + } + + if err != nil { if isAutomaticRetryOnReason { if isRetry, retryReasonPattern := shouldRetry(outBuffer.String()); isRetry { log.Warnf("Automatic retry reason found in log: %s - retrying...", retryReasonPattern) - return runGradleTask(gradleTool, buildFile, tasks, options, false) + return runGradleTask(gradleTool, buildFile, tasks, options, false, destDir) } } return err @@ -137,6 +150,16 @@ func runGradleTask(gradleTool, buildFile, tasks, options string, isAutomaticRetr return nil } +func shouldSaveOutputToLogFile(options []string) bool { + for _, option := range options { + if option == "--debug" || option == "-d" { + return true + } + } + + return false +} + func filterEmpty(in []string) (out []string) { for _, item := range in { if strings.TrimSpace(item) != "" { @@ -247,7 +270,7 @@ func main() { gradleStarted := time.Now() log.Infof("Running gradle task...") - if err := runGradleTask(gradlewPath, configs.GradleFile, configs.GradleTasks, configs.GradleOptions, configs.RetryOnFailure); err != nil { + if err := runGradleTask(gradlewPath, configs.GradleFile, configs.GradleTasks, configs.GradleOptions, configs.RetryOnFailure, configs.DeployDir); err != nil { failf("Gradle task failed, error: %s", err) } diff --git a/step.yml b/step.yml index 8a1d9dd..4cf5573 100644 --- a/step.yml +++ b/step.yml @@ -192,6 +192,9 @@ inputs: You can use multiple options, separated by a space character. Example: `--stacktrace --debug` + + If `--debug` or `-d` options are set then only the last 20 lines of the raw gradle output will be visible in the build log. + The full raw output will be exported to the $BITRISE_GRADLE_RAW_RESULT_TEXT_PATH variable and will be added as an artifact. - retry_on_failure: "yes" opts: category: Debug diff --git a/vendor/github.com/bitrise-io/go-steputils/commandhelper/commandhelper.go b/vendor/github.com/bitrise-io/go-steputils/commandhelper/commandhelper.go new file mode 100644 index 0000000..5b11548 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-steputils/commandhelper/commandhelper.go @@ -0,0 +1,61 @@ +package commandhelper + +import ( + "bytes" + "fmt" + + "github.com/bitrise-io/go-steputils/output" + "github.com/bitrise-io/go-utils/colorstring" + "github.com/bitrise-io/go-utils/command" + "github.com/bitrise-io/go-utils/log" +) + +// RunAndExportOutputWithReturningLastNLines runs a command and captures it's output to a file. +// The genereated output file will be exported to the envKey environment variable. +// It returns the last N lines of the output, the error of the command and if any error happened during +// exporting the output file. +func RunAndExportOutputWithReturningLastNLines(cmd command.Model, destinationPath, envKey string, lines int) (string, error, error) { + var outBuffer bytes.Buffer + cmd.SetStdout(&outBuffer) + cmd.SetStderr(&outBuffer) + + cmdError := cmd.Run() + rawOutput := outBuffer.String() + + lastLines, err := output.ExportOutputFileContentAndReturnLastNLines(rawOutput, destinationPath, envKey, lines) + if err != nil { + return "", cmdError, err + } + + return lastLines, cmdError, nil +} + +// RunAndExportOutput runs a command and captures it's output to a file. +// The genereated output file will be exported to the envKey environment variable. +// The last N lines of the output if loged with some description. +func RunAndExportOutput(cmd command.Model, destinationPath, envKey string, lines int) error { + outputLines, cmdErr, exportErr := RunAndExportOutputWithReturningLastNLines(cmd, destinationPath, envKey, lines) + + if exportErr != nil { + log.Warnf("Failed to export %s, error: %s", envKey, exportErr) + } + + if lines > 0 && len(outputLines) > 0 { + lastLines := "You can find the last couple of lines of output below.:" + if cmdErr != nil { + log.Errorf(lastLines) + } else { + log.Infof(lastLines) + } + + log.Printf(outputLines) + + if cmdErr != nil { + log.Warnf("If you can't find the reason of the error in the log, please check the %s.", destinationPath) + } + } + + log.Infof(colorstring.Magenta(fmt.Sprintf(`The log file is stored in %s, and its full path is available in the $%s environment variable.`, destinationPath, envKey))) + + return cmdErr +} diff --git a/vendor/github.com/bitrise-io/go-steputils/output/output.go b/vendor/github.com/bitrise-io/go-steputils/output/output.go new file mode 100644 index 0000000..a60e806 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-steputils/output/output.go @@ -0,0 +1,104 @@ +package output + +import ( + "fmt" + "path/filepath" + + "github.com/bitrise-io/go-steputils/tools" + "github.com/bitrise-io/go-utils/command" + "github.com/bitrise-io/go-utils/fileutil" + "github.com/bitrise-io/go-utils/pathutil" + "github.com/bitrise-io/go-utils/stringutil" + "github.com/bitrise-io/go-utils/ziputil" +) + +// ExportOutputDir ... +func ExportOutputDir(sourceDir, destinationDir, envKey string) error { + absSourceDir, err := pathutil.AbsPath(sourceDir) + if err != nil { + return err + } + + absDestinationDir, err := pathutil.AbsPath(destinationDir) + if err != nil { + return err + } + + if absSourceDir != absDestinationDir { + if err := command.CopyDir(absSourceDir, absDestinationDir, true); err != nil { + return err + } + } + return tools.ExportEnvironmentWithEnvman(envKey, absDestinationDir) +} + +// ExportOutputFile ... +func ExportOutputFile(sourcePth, destinationPth, envKey string) error { + absSourcePth, err := pathutil.AbsPath(sourcePth) + if err != nil { + return err + } + + absDestinationPth, err := pathutil.AbsPath(destinationPth) + if err != nil { + return err + } + + if absSourcePth != absDestinationPth { + if err := command.CopyFile(absSourcePth, absDestinationPth); err != nil { + return err + } + } + return tools.ExportEnvironmentWithEnvman(envKey, absDestinationPth) +} + +// ExportOutputFileContent ... +func ExportOutputFileContent(content, destinationPth, envKey string) error { + if err := fileutil.WriteStringToFile(destinationPth, content); err != nil { + return err + } + + return ExportOutputFile(destinationPth, destinationPth, envKey) +} + +// ExportOutputFileContentAndReturnLastNLines ... +func ExportOutputFileContentAndReturnLastNLines(content, destinationPath, envKey string, lines int) (string, error) { + if err := fileutil.WriteStringToFile(destinationPath, content); err != nil { + return "", err + } + + if err := ExportOutputFile(destinationPath, destinationPath, envKey); err != nil { + return "", err + } + + return stringutil.LastNLines(content, lines), nil +} + +// ZipAndExportOutput ... +func ZipAndExportOutput(sourcePth, destinationZipPth, envKey string) error { + tmpDir, err := pathutil.NormalizedOSTempDirPath("__export_tmp_dir__") + if err != nil { + return err + } + + base := filepath.Base(sourcePth) + tmpZipFilePth := filepath.Join(tmpDir, base+".zip") + + if exist, err := pathutil.IsDirExists(sourcePth); err != nil { + return err + } else if exist { + if err := ziputil.ZipDir(sourcePth, tmpZipFilePth, false); err != nil { + return err + } + } else if exist, err := pathutil.IsPathExists(sourcePth); err != nil { + return err + } else if exist { + if err := ziputil.ZipFile(sourcePth, tmpZipFilePth); err != nil { + return err + } + } else { + return fmt.Errorf("source path (%s) not exists", sourcePth) + } + + return ExportOutputFile(tmpZipFilePth, destinationZipPth, envKey) +} diff --git a/vendor/github.com/bitrise-io/go-utils/pkcs12/README.md b/vendor/github.com/bitrise-io/go-utils/pkcs12/README.md new file mode 100644 index 0000000..f10f9f1 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/pkcs12/README.md @@ -0,0 +1,35 @@ +# package pkcs12 + +[![GoDoc](https://godoc.org/software.sslmate.com/src/go-pkcs12?status.svg)](https://godoc.org/software.sslmate.com/src/go-pkcs12) + + import "software.sslmate.com/src/go-pkcs12" + +Package pkcs12 implements some of PKCS#12 (also known as P12 or PFX). +It is intended for decoding P12/PFX files for use with the `crypto/tls` +package, and for encoding P12/PFX files for use by legacy applications which +do not support newer formats. Since PKCS#12 uses weak encryption +primitives, it SHOULD NOT be used for new applications. + +This package is forked from `golang.org/x/crypto/pkcs12`, which is frozen. +The implementation is distilled from https://tools.ietf.org/html/rfc7292 +and referenced documents. + +This repository holds supplementary Go cryptography libraries. + +## Import Path + +Note that although the source code and issue tracker for this package are hosted +on GitHub, the import path is: + + software.sslmate.com/src/go-pkcs12 + +Please be sure to use this path when you `go get` and `import` this package. + +## Download/Install + +The easiest way to install is to run `go get -u software.sslmate.com/src/go-pkcs12`. You +can also manually git clone the repository to `$GOPATH/src/software.sslmate.com/src/go-pkcs12`. + +## Report Issues / Send Patches + +Open an issue or PR at https://github.com/SSLMate/go-pkcs12 diff --git a/vendor/github.com/bitrise-io/go-utils/pkcs12/bmp-string.go b/vendor/github.com/bitrise-io/go-utils/pkcs12/bmp-string.go new file mode 100644 index 0000000..233b8b6 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/pkcs12/bmp-string.go @@ -0,0 +1,50 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "errors" + "unicode/utf16" +) + +// bmpString returns s encoded in UCS-2 with a zero terminator. +func bmpString(s string) ([]byte, error) { + // References: + // https://tools.ietf.org/html/rfc7292#appendix-B.1 + // https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane + // - non-BMP characters are encoded in UTF 16 by using a surrogate pair of 16-bit codes + // EncodeRune returns 0xfffd if the rune does not need special encoding + // - the above RFC provides the info that BMPStrings are NULL terminated. + + ret := make([]byte, 0, 2*len(s)+2) + + for _, r := range s { + if t, _ := utf16.EncodeRune(r); t != 0xfffd { + return nil, errors.New("pkcs12: string contains characters that cannot be encoded in UCS-2") + } + ret = append(ret, byte(r/256), byte(r%256)) + } + + return append(ret, 0, 0), nil +} + +func decodeBMPString(bmpString []byte) (string, error) { + if len(bmpString)%2 != 0 { + return "", errors.New("pkcs12: odd-length BMP string") + } + + // strip terminator if present + if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 { + bmpString = bmpString[:l-2] + } + + s := make([]uint16, 0, len(bmpString)/2) + for len(bmpString) > 0 { + s = append(s, uint16(bmpString[0])<<8+uint16(bmpString[1])) + bmpString = bmpString[2:] + } + + return string(utf16.Decode(s)), nil +} diff --git a/vendor/github.com/bitrise-io/go-utils/pkcs12/crypto.go b/vendor/github.com/bitrise-io/go-utils/pkcs12/crypto.go new file mode 100644 index 0000000..30f9338 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/pkcs12/crypto.go @@ -0,0 +1,173 @@ +// Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "bytes" + "crypto/cipher" + "crypto/des" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + + "github.com/bitrise-io/go-utils/pkcs12/internal/rc2" +) + +var ( + oidPBEWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) + oidPBEWithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 6}) +) + +// pbeCipher is an abstraction of a PKCS#12 cipher. +type pbeCipher interface { + // create returns a cipher.Block given a key. + create(key []byte) (cipher.Block, error) + // deriveKey returns a key derived from the given password and salt. + deriveKey(salt, password []byte, iterations int) []byte + // deriveKey returns an IV derived from the given password and salt. + deriveIV(salt, password []byte, iterations int) []byte +} + +type shaWithTripleDESCBC struct{} + +func (shaWithTripleDESCBC) create(key []byte) (cipher.Block, error) { + return des.NewTripleDESCipher(key) +} + +func (shaWithTripleDESCBC) deriveKey(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 24) +} + +func (shaWithTripleDESCBC) deriveIV(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) +} + +type shaWith40BitRC2CBC struct{} + +func (shaWith40BitRC2CBC) create(key []byte) (cipher.Block, error) { + return rc2.New(key, len(key)*8) +} + +func (shaWith40BitRC2CBC) deriveKey(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 5) +} + +func (shaWith40BitRC2CBC) deriveIV(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) +} + +type pbeParams struct { + Salt []byte + Iterations int +} + +func pbeCipherFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.Block, []byte, error) { + var cipherType pbeCipher + + switch { + case algorithm.Algorithm.Equal(oidPBEWithSHAAnd3KeyTripleDESCBC): + cipherType = shaWithTripleDESCBC{} + case algorithm.Algorithm.Equal(oidPBEWithSHAAnd40BitRC2CBC): + cipherType = shaWith40BitRC2CBC{} + default: + return nil, nil, NotImplementedError("algorithm " + algorithm.Algorithm.String() + " is not supported") + } + + var params pbeParams + if err := unmarshal(algorithm.Parameters.FullBytes, ¶ms); err != nil { + return nil, nil, err + } + + key := cipherType.deriveKey(params.Salt, password, params.Iterations) + iv := cipherType.deriveIV(params.Salt, password, params.Iterations) + + block, err := cipherType.create(key) + if err != nil { + return nil, nil, err + } + + return block, iv, nil +} + +func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, int, error) { + block, iv, err := pbeCipherFor(algorithm, password) + if err != nil { + return nil, 0, err + } + + return cipher.NewCBCDecrypter(block, iv), block.BlockSize(), nil +} + +func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) { + cbc, blockSize, err := pbDecrypterFor(info.Algorithm(), password) + if err != nil { + return nil, err + } + + encrypted := info.Data() + if len(encrypted) == 0 { + return nil, errors.New("pkcs12: empty encrypted data") + } + if len(encrypted)%blockSize != 0 { + return nil, errors.New("pkcs12: input is not a multiple of the block size") + } + decrypted = make([]byte, len(encrypted)) + cbc.CryptBlocks(decrypted, encrypted) + + psLen := int(decrypted[len(decrypted)-1]) + if psLen == 0 || psLen > blockSize { + return nil, ErrDecryption + } + + if len(decrypted) < psLen { + return nil, ErrDecryption + } + ps := decrypted[len(decrypted)-psLen:] + decrypted = decrypted[:len(decrypted)-psLen] + if bytes.Compare(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) != 0 { + return nil, ErrDecryption + } + + return +} + +// decryptable abstracts an object that contains ciphertext. +type decryptable interface { + Algorithm() pkix.AlgorithmIdentifier + Data() []byte +} + +func pbEncrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, int, error) { + block, iv, err := pbeCipherFor(algorithm, password) + if err != nil { + return nil, 0, err + } + + return cipher.NewCBCEncrypter(block, iv), block.BlockSize(), nil +} + +func pbEncrypt(info encryptable, decrypted []byte, password []byte) error { + cbc, blockSize, err := pbEncrypterFor(info.Algorithm(), password) + if err != nil { + return err + } + + psLen := blockSize - len(decrypted)%blockSize + encrypted := make([]byte, len(decrypted)+psLen) + copy(encrypted[:len(decrypted)], decrypted) + copy(encrypted[len(decrypted):], bytes.Repeat([]byte{byte(psLen)}, psLen)) + cbc.CryptBlocks(encrypted, encrypted) + + info.SetData(encrypted) + + return nil +} + +// encryptable abstracts a object that contains ciphertext. +type encryptable interface { + Algorithm() pkix.AlgorithmIdentifier + SetData([]byte) +} diff --git a/vendor/github.com/bitrise-io/go-utils/pkcs12/errors.go b/vendor/github.com/bitrise-io/go-utils/pkcs12/errors.go new file mode 100644 index 0000000..7377ce6 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/pkcs12/errors.go @@ -0,0 +1,23 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import "errors" + +var ( + // ErrDecryption represents a failure to decrypt the input. + ErrDecryption = errors.New("pkcs12: decryption error, incorrect padding") + + // ErrIncorrectPassword is returned when an incorrect password is detected. + // Usually, P12/PFX data is signed to be able to verify the password. + ErrIncorrectPassword = errors.New("pkcs12: decryption password incorrect") +) + +// NotImplementedError indicates that the input is not currently supported. +type NotImplementedError string + +func (e NotImplementedError) Error() string { + return "pkcs12: " + string(e) +} diff --git a/vendor/github.com/bitrise-io/go-utils/pkcs12/internal/rc2/rc2.go b/vendor/github.com/bitrise-io/go-utils/pkcs12/internal/rc2/rc2.go new file mode 100644 index 0000000..7499e3f --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/pkcs12/internal/rc2/rc2.go @@ -0,0 +1,271 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package rc2 implements the RC2 cipher +/* +https://www.ietf.org/rfc/rfc2268.txt +http://people.csail.mit.edu/rivest/pubs/KRRR98.pdf + +This code is licensed under the MIT license. +*/ +package rc2 + +import ( + "crypto/cipher" + "encoding/binary" +) + +// The rc2 block size in bytes +const BlockSize = 8 + +type rc2Cipher struct { + k [64]uint16 +} + +// New returns a new rc2 cipher with the given key and effective key length t1 +func New(key []byte, t1 int) (cipher.Block, error) { + // TODO(dgryski): error checking for key length + return &rc2Cipher{ + k: expandKey(key, t1), + }, nil +} + +func (*rc2Cipher) BlockSize() int { return BlockSize } + +var piTable = [256]byte{ + 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, + 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, + 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32, + 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82, + 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, + 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26, + 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03, + 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, + 0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, + 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec, + 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, + 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31, + 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9, + 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, + 0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, + 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad, +} + +func expandKey(key []byte, t1 int) [64]uint16 { + + l := make([]byte, 128) + copy(l, key) + + var t = len(key) + var t8 = (t1 + 7) / 8 + var tm = byte(255 % uint(1<<(8+uint(t1)-8*uint(t8)))) + + for i := len(key); i < 128; i++ { + l[i] = piTable[l[i-1]+l[uint8(i-t)]] + } + + l[128-t8] = piTable[l[128-t8]&tm] + + for i := 127 - t8; i >= 0; i-- { + l[i] = piTable[l[i+1]^l[i+t8]] + } + + var k [64]uint16 + + for i := range k { + k[i] = uint16(l[2*i]) + uint16(l[2*i+1])*256 + } + + return k +} + +func rotl16(x uint16, b uint) uint16 { + return (x >> (16 - b)) | (x << b) +} + +func (c *rc2Cipher) Encrypt(dst, src []byte) { + + r0 := binary.LittleEndian.Uint16(src[0:]) + r1 := binary.LittleEndian.Uint16(src[2:]) + r2 := binary.LittleEndian.Uint16(src[4:]) + r3 := binary.LittleEndian.Uint16(src[6:]) + + var j int + + for j <= 16 { + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + + } + + r0 = r0 + c.k[r3&63] + r1 = r1 + c.k[r0&63] + r2 = r2 + c.k[r1&63] + r3 = r3 + c.k[r2&63] + + for j <= 40 { + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + + } + + r0 = r0 + c.k[r3&63] + r1 = r1 + c.k[r0&63] + r2 = r2 + c.k[r1&63] + r3 = r3 + c.k[r2&63] + + for j <= 60 { + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + } + + binary.LittleEndian.PutUint16(dst[0:], r0) + binary.LittleEndian.PutUint16(dst[2:], r1) + binary.LittleEndian.PutUint16(dst[4:], r2) + binary.LittleEndian.PutUint16(dst[6:], r3) +} + +func (c *rc2Cipher) Decrypt(dst, src []byte) { + + r0 := binary.LittleEndian.Uint16(src[0:]) + r1 := binary.LittleEndian.Uint16(src[2:]) + r2 := binary.LittleEndian.Uint16(src[4:]) + r3 := binary.LittleEndian.Uint16(src[6:]) + + j := 63 + + for j >= 44 { + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + } + + r3 = r3 - c.k[r2&63] + r2 = r2 - c.k[r1&63] + r1 = r1 - c.k[r0&63] + r0 = r0 - c.k[r3&63] + + for j >= 20 { + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + + } + + r3 = r3 - c.k[r2&63] + r2 = r2 - c.k[r1&63] + r1 = r1 - c.k[r0&63] + r0 = r0 - c.k[r3&63] + + for j >= 0 { + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + + } + + binary.LittleEndian.PutUint16(dst[0:], r0) + binary.LittleEndian.PutUint16(dst[2:], r1) + binary.LittleEndian.PutUint16(dst[4:], r2) + binary.LittleEndian.PutUint16(dst[6:], r3) +} diff --git a/vendor/github.com/bitrise-io/go-utils/pkcs12/mac.go b/vendor/github.com/bitrise-io/go-utils/pkcs12/mac.go new file mode 100644 index 0000000..b7b05de --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/pkcs12/mac.go @@ -0,0 +1,60 @@ +// Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "crypto/hmac" + "crypto/sha1" + "crypto/x509/pkix" + "encoding/asn1" +) + +type macData struct { + Mac digestInfo + MacSalt []byte + Iterations int `asn1:"optional,default:1"` +} + +// from PKCS#7: +type digestInfo struct { + Algorithm pkix.AlgorithmIdentifier + Digest []byte +} + +var ( + oidSHA1 = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) +) + +func verifyMac(macData *macData, message, password []byte) error { + if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) { + return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) + } + + key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20) + + mac := hmac.New(sha1.New, key) + mac.Write(message) + expectedMAC := mac.Sum(nil) + + if !hmac.Equal(macData.Mac.Digest, expectedMAC) { + return ErrIncorrectPassword + } + return nil +} + +func computeMac(macData *macData, message, password []byte) error { + if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) { + return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) + } + + key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20) + + mac := hmac.New(sha1.New, key) + mac.Write(message) + macData.Mac.Digest = mac.Sum(nil) + + return nil +} diff --git a/vendor/github.com/bitrise-io/go-utils/pkcs12/pbkdf.go b/vendor/github.com/bitrise-io/go-utils/pkcs12/pbkdf.go new file mode 100644 index 0000000..5c419d4 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/pkcs12/pbkdf.go @@ -0,0 +1,170 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "bytes" + "crypto/sha1" + "math/big" +) + +var ( + one = big.NewInt(1) +) + +// sha1Sum returns the SHA-1 hash of in. +func sha1Sum(in []byte) []byte { + sum := sha1.Sum(in) + return sum[:] +} + +// fillWithRepeats returns v*ceiling(len(pattern) / v) bytes consisting of +// repeats of pattern. +func fillWithRepeats(pattern []byte, v int) []byte { + if len(pattern) == 0 { + return nil + } + outputLen := v * ((len(pattern) + v - 1) / v) + return bytes.Repeat(pattern, (outputLen+len(pattern)-1)/len(pattern))[:outputLen] +} + +func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID byte, size int) (key []byte) { + // implementation of https://tools.ietf.org/html/rfc7292#appendix-B.2 , RFC text verbatim in comments + + // Let H be a hash function built around a compression function f: + + // Z_2^u x Z_2^v -> Z_2^u + + // (that is, H has a chaining variable and output of length u bits, and + // the message input to the compression function of H is v bits). The + // values for u and v are as follows: + + // HASH FUNCTION VALUE u VALUE v + // MD2, MD5 128 512 + // SHA-1 160 512 + // SHA-224 224 512 + // SHA-256 256 512 + // SHA-384 384 1024 + // SHA-512 512 1024 + // SHA-512/224 224 1024 + // SHA-512/256 256 1024 + + // Furthermore, let r be the iteration count. + + // We assume here that u and v are both multiples of 8, as are the + // lengths of the password and salt strings (which we denote by p and s, + // respectively) and the number n of pseudorandom bits required. In + // addition, u and v are of course non-zero. + + // For information on security considerations for MD5 [19], see [25] and + // [1], and on those for MD2, see [18]. + + // The following procedure can be used to produce pseudorandom bits for + // a particular "purpose" that is identified by a byte called "ID". + // This standard specifies 3 different values for the ID byte: + + // 1. If ID=1, then the pseudorandom bits being produced are to be used + // as key material for performing encryption or decryption. + + // 2. If ID=2, then the pseudorandom bits being produced are to be used + // as an IV (Initial Value) for encryption or decryption. + + // 3. If ID=3, then the pseudorandom bits being produced are to be used + // as an integrity key for MACing. + + // 1. Construct a string, D (the "diversifier"), by concatenating v/8 + // copies of ID. + var D []byte + for i := 0; i < v; i++ { + D = append(D, ID) + } + + // 2. Concatenate copies of the salt together to create a string S of + // length v(ceiling(s/v)) bits (the final copy of the salt may be + // truncated to create S). Note that if the salt is the empty + // string, then so is S. + + S := fillWithRepeats(salt, v) + + // 3. Concatenate copies of the password together to create a string P + // of length v(ceiling(p/v)) bits (the final copy of the password + // may be truncated to create P). Note that if the password is the + // empty string, then so is P. + + P := fillWithRepeats(password, v) + + // 4. Set I=S||P to be the concatenation of S and P. + I := append(S, P...) + + // 5. Set c=ceiling(n/u). + c := (size + u - 1) / u + + // 6. For i=1, 2, ..., c, do the following: + A := make([]byte, c*20) + var IjBuf []byte + for i := 0; i < c; i++ { + // A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1, + // H(H(H(... H(D||I)))) + Ai := hash(append(D, I...)) + for j := 1; j < r; j++ { + Ai = hash(Ai) + } + copy(A[i*20:], Ai[:]) + + if i < c-1 { // skip on last iteration + // B. Concatenate copies of Ai to create a string B of length v + // bits (the final copy of Ai may be truncated to create B). + var B []byte + for len(B) < v { + B = append(B, Ai[:]...) + } + B = B[:v] + + // C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit + // blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by + // setting I_j=(I_j+B+1) mod 2^v for each j. + { + Bbi := new(big.Int).SetBytes(B) + Ij := new(big.Int) + + for j := 0; j < len(I)/v; j++ { + Ij.SetBytes(I[j*v : (j+1)*v]) + Ij.Add(Ij, Bbi) + Ij.Add(Ij, one) + Ijb := Ij.Bytes() + // We expect Ijb to be exactly v bytes, + // if it is longer or shorter we must + // adjust it accordingly. + if len(Ijb) > v { + Ijb = Ijb[len(Ijb)-v:] + } + if len(Ijb) < v { + if IjBuf == nil { + IjBuf = make([]byte, v) + } + bytesShort := v - len(Ijb) + for i := 0; i < bytesShort; i++ { + IjBuf[i] = 0 + } + copy(IjBuf[bytesShort:], Ijb) + Ijb = IjBuf + } + copy(I[j*v:(j+1)*v], Ijb) + } + } + } + } + // 7. Concatenate A_1, A_2, ..., A_c together to form a pseudorandom + // bit string, A. + + // 8. Use the first n bits of A as the output of this entire process. + return A[:size] + + // If the above process is being used to generate a DES key, the process + // should be used to create 64 random bits, and the key's parity bits + // should be set after the 64 bits have been produced. Similar concerns + // hold for 2-key and 3-key triple-DES keys, for CDMF keys, and for any + // similar keys with parity bits "built into them". +} diff --git a/vendor/github.com/bitrise-io/go-utils/pkcs12/pkcs12.go b/vendor/github.com/bitrise-io/go-utils/pkcs12/pkcs12.go new file mode 100644 index 0000000..c257a3e --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/pkcs12/pkcs12.go @@ -0,0 +1,556 @@ +// Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package pkcs12 implements some of PKCS#12 (also known as P12 or PFX). +// It is intended for decoding P12/PFX files for use with the crypto/tls +// package, and for encoding P12/PFX files for use by legacy applications which +// do not support newer formats. Since PKCS#12 uses weak encryption +// primitives, it SHOULD NOT be used for new applications. +// +// This package is forked from golang.org/x/crypto/pkcs12, which is frozen. +// The implementation is distilled from https://tools.ietf.org/html/rfc7292 +// and referenced documents. +package pkcs12 + +import ( + "crypto/ecdsa" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/hex" + "encoding/pem" + "errors" + "io" +) + +// DefaultPassword is the string "changeit", a commonly-used password for +// PKCS#12 files. Due to the weak encryption used by PKCS#12, it is +// RECOMMENDED that you use DefaultPassword when encoding PKCS#12 files, +// and protect the PKCS#12 files using other means. +const DefaultPassword = "changeit" + +var ( + oidDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 1}) + oidEncryptedDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 6}) + + oidFriendlyName = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20}) + oidLocalKeyID = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21}) + oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1}) +) + +type pfxPdu struct { + Version int + AuthSafe contentInfo + MacData macData `asn1:"optional"` +} + +type contentInfo struct { + ContentType asn1.ObjectIdentifier + Content asn1.RawValue `asn1:"tag:0,explicit,optional"` +} + +type encryptedData struct { + Version int + EncryptedContentInfo encryptedContentInfo +} + +type encryptedContentInfo struct { + ContentType asn1.ObjectIdentifier + ContentEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedContent []byte `asn1:"tag:0,optional"` +} + +func (i encryptedContentInfo) Algorithm() pkix.AlgorithmIdentifier { + return i.ContentEncryptionAlgorithm +} + +func (i encryptedContentInfo) Data() []byte { return i.EncryptedContent } + +func (i *encryptedContentInfo) SetData(data []byte) { i.EncryptedContent = data } + +type safeBag struct { + Id asn1.ObjectIdentifier + Value asn1.RawValue `asn1:"tag:0,explicit"` + Attributes []pkcs12Attribute `asn1:"set,optional"` +} + +type pkcs12Attribute struct { + Id asn1.ObjectIdentifier + Value asn1.RawValue `asn1:"set"` +} + +type encryptedPrivateKeyInfo struct { + AlgorithmIdentifier pkix.AlgorithmIdentifier + EncryptedData []byte +} + +func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier { + return i.AlgorithmIdentifier +} + +func (i encryptedPrivateKeyInfo) Data() []byte { + return i.EncryptedData +} + +func (i *encryptedPrivateKeyInfo) SetData(data []byte) { + i.EncryptedData = data +} + +// PEM block types +const ( + certificateType = "CERTIFICATE" + privateKeyType = "PRIVATE KEY" +) + +// unmarshal calls asn1.Unmarshal, but also returns an error if there is any +// trailing data after unmarshaling. +func unmarshal(in []byte, out interface{}) error { + trailing, err := asn1.Unmarshal(in, out) + if err != nil { + return err + } + if len(trailing) != 0 { + return errors.New("pkcs12: trailing data found") + } + return nil +} + +// ToPEM converts all "safe bags" contained in pfxData to PEM blocks. +// DO NOT USE THIS FUNCTION. ToPEM creates invalid PEM blocks; private keys +// are encoded as raw RSA or EC private keys rather than PKCS#8 despite being +// labeled "PRIVATE KEY". To decode a PKCS#12 file, use DecodeChain instead, +// and use the encoding/pem package to convert to PEM if necessary. +func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, ErrIncorrectPassword + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword) + + if err != nil { + return nil, err + } + + blocks := make([]*pem.Block, 0, len(bags)) + for _, bag := range bags { + block, err := convertBag(&bag, encodedPassword) + if err != nil { + return nil, err + } + blocks = append(blocks, block) + } + + return blocks, nil +} + +func convertBag(bag *safeBag, password []byte) (*pem.Block, error) { + block := &pem.Block{ + Headers: make(map[string]string), + } + + for _, attribute := range bag.Attributes { + k, v, err := convertAttribute(&attribute) + if err != nil { + return nil, err + } + block.Headers[k] = v + } + + switch { + case bag.Id.Equal(oidCertBag): + block.Type = certificateType + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, err + } + block.Bytes = certsData + case bag.Id.Equal(oidPKCS8ShroundedKeyBag): + block.Type = privateKeyType + + key, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, password) + if err != nil { + return nil, err + } + + switch key := key.(type) { + case *rsa.PrivateKey: + block.Bytes = x509.MarshalPKCS1PrivateKey(key) + case *ecdsa.PrivateKey: + block.Bytes, err = x509.MarshalECPrivateKey(key) + if err != nil { + return nil, err + } + default: + return nil, errors.New("found unknown private key type in PKCS#8 wrapping") + } + default: + return nil, errors.New("don't know how to convert a safe bag of type " + bag.Id.String()) + } + return block, nil +} + +func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) { + isString := false + + switch { + case attribute.Id.Equal(oidFriendlyName): + key = "friendlyName" + isString = true + case attribute.Id.Equal(oidLocalKeyID): + key = "localKeyId" + case attribute.Id.Equal(oidMicrosoftCSPName): + // This key is chosen to match OpenSSL. + key = "Microsoft CSP Name" + isString = true + default: + return "", "", errors.New("pkcs12: unknown attribute with OID " + attribute.Id.String()) + } + + if isString { + if err := unmarshal(attribute.Value.Bytes, &attribute.Value); err != nil { + return "", "", err + } + if value, err = decodeBMPString(attribute.Value.Bytes); err != nil { + return "", "", err + } + } else { + var id []byte + if err := unmarshal(attribute.Value.Bytes, &id); err != nil { + return "", "", err + } + value = hex.EncodeToString(id) + } + + return key, value, nil +} + +// Decode extracts a certificate and private key from pfxData. This function +// assumes that there is only one certificate and only one private key in the +// pfxData. Since PKCS#12 files often contain more than one certificate, you +// probably want to use DecodeChain instead. +func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error) { + var caCerts []*x509.Certificate + privateKey, certificate, caCerts, err = DecodeChain(pfxData, password) + if len(caCerts) != 0 { + err = errors.New("pkcs12: expected exactly two safe bags in the PFX PDU") + } + return +} + +// DecodeChain extracts a certificate, a CA certificate chain, and private key +// from pfxData. This function assumes that there is at least one certificate +// and only one private key in the pfxData. The first certificate is assumed to +// be the leaf certificate, and subsequent certificates, if any, are assumed to +// comprise the CA certificate chain. +func DecodeChain(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, caCerts []*x509.Certificate, err error) { + certificates, privateKeys, err := DecodeAll(pfxData, password) + if err != nil { + return nil, nil, nil, err + } + + if len(certificates) == 0 { + return nil, nil, nil, errors.New("pkcs12: certificate missing") + } + + certificate = certificates[0] + if len(certificates) > 1 { + caCerts = certificates[1:] + } + + if len(privateKeys) == 0 { + return nil, nil, nil, errors.New("pkcs12: private key missing") + } + if len(privateKeys) > 1 { + return nil, nil, nil, errors.New("pkcs12: expected exactly one key bag") + } + + privateKey = privateKeys[0] + + return +} + +// DecodeAll extracts all certificates and private keys from pfxData. +func DecodeAll(pfxData []byte, password string) ([]*x509.Certificate, []interface{}, error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, nil, err + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword) + if err != nil { + return nil, nil, err + } + + var certificates []*x509.Certificate + var privateKeys []interface{} + for _, bag := range bags { + switch { + case bag.Id.Equal(oidCertBag): + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, nil, err + } + certs, err := x509.ParseCertificates(certsData) + if err != nil { + return nil, nil, err + } + if len(certs) != 1 { + err = errors.New("pkcs12: expected exactly one certificate in the certBag") + return nil, nil, err + } + certificates = append(certificates, certs[0]) + + case bag.Id.Equal(oidPKCS8ShroundedKeyBag): + privateKey, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword) + if err != nil { + return nil, nil, err + } + privateKeys = append(privateKeys, privateKey) + } + } + + return certificates, privateKeys, nil +} + +func getSafeContents(p12Data, password []byte) (bags []safeBag, updatedPassword []byte, err error) { + pfx := new(pfxPdu) + if err := unmarshal(p12Data, pfx); err != nil { + return nil, nil, errors.New("pkcs12: error reading P12 data: " + err.Error()) + } + + if pfx.Version != 3 { + return nil, nil, NotImplementedError("can only decode v3 PFX PDU's") + } + + if !pfx.AuthSafe.ContentType.Equal(oidDataContentType) { + return nil, nil, NotImplementedError("only password-protected PFX is implemented") + } + + // unmarshal the explicit bytes in the content for type 'data' + if err := unmarshal(pfx.AuthSafe.Content.Bytes, &pfx.AuthSafe.Content); err != nil { + return nil, nil, err + } + + if len(pfx.MacData.Mac.Algorithm.Algorithm) == 0 { + return nil, nil, errors.New("pkcs12: no MAC in data") + } + + if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil { + if err == ErrIncorrectPassword && len(password) == 2 && password[0] == 0 && password[1] == 0 { + // some implementations use an empty byte array + // for the empty string password try one more + // time with empty-empty password + password = nil + err = verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password) + } + if err != nil { + return nil, nil, err + } + } + + var authenticatedSafe []contentInfo + if err := unmarshal(pfx.AuthSafe.Content.Bytes, &authenticatedSafe); err != nil { + return nil, nil, err + } + + if len(authenticatedSafe) != 2 { + return nil, nil, NotImplementedError("expected exactly two items in the authenticated safe") + } + + for _, ci := range authenticatedSafe { + var data []byte + + switch { + case ci.ContentType.Equal(oidDataContentType): + if err := unmarshal(ci.Content.Bytes, &data); err != nil { + return nil, nil, err + } + case ci.ContentType.Equal(oidEncryptedDataContentType): + var encryptedData encryptedData + if err := unmarshal(ci.Content.Bytes, &encryptedData); err != nil { + return nil, nil, err + } + if encryptedData.Version != 0 { + return nil, nil, NotImplementedError("only version 0 of EncryptedData is supported") + } + if data, err = pbDecrypt(encryptedData.EncryptedContentInfo, password); err != nil { + return nil, nil, err + } + default: + return nil, nil, NotImplementedError("only data and encryptedData content types are supported in authenticated safe") + } + + var safeContents []safeBag + if err := unmarshal(data, &safeContents); err != nil { + return nil, nil, err + } + bags = append(bags, safeContents...) + } + + return bags, password, nil +} + +// Encode produces pfxData containing one private key (privateKey), an +// end-entity certificate (certificate), and any number of CA certificates +// (caCerts). +// +// The private key is encrypted with the provided password, but due to the +// weak encryption primitives used by PKCS#12, it is RECOMMENDED that you +// specify a hard-coded password (such as pkcs12.DefaultPassword) and protect +// the resulting pfxData using other means. +// +// The rand argument is used to provide entropy for the encryption, and +// can be set to rand.Reader from the crypto/rand package. +// +// Encode emulates the behavior of OpenSSL's PKCS12_create: it creates two +// SafeContents: one that's encrypted with RC2 and contains the certificates, +// and another that is unencrypted and contains the private key shrouded with +// 3DES The private key bag and the end-entity certificate bag have the +// LocalKeyId attribute set to the SHA-1 fingerprint of the end-entity +// certificate. +func Encode(rand io.Reader, privateKey interface{}, certificate *x509.Certificate, caCerts []*x509.Certificate, password string) (pfxData []byte, err error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, err + } + + var pfx pfxPdu + pfx.Version = 3 + + var certFingerprint = sha1.Sum(certificate.Raw) + var localKeyIdAttr pkcs12Attribute + localKeyIdAttr.Id = oidLocalKeyID + localKeyIdAttr.Value.Class = 0 + localKeyIdAttr.Value.Tag = 17 + localKeyIdAttr.Value.IsCompound = true + if localKeyIdAttr.Value.Bytes, err = asn1.Marshal(certFingerprint[:]); err != nil { + return nil, err + } + + var certBags []safeBag + var certBag *safeBag + if certBag, err = makeCertBag(certificate.Raw, []pkcs12Attribute{localKeyIdAttr}); err != nil { + return nil, err + } + certBags = append(certBags, *certBag) + + for _, cert := range caCerts { + if certBag, err = makeCertBag(cert.Raw, []pkcs12Attribute{}); err != nil { + return nil, err + } + certBags = append(certBags, *certBag) + } + + var keyBag safeBag + keyBag.Id = oidPKCS8ShroundedKeyBag + keyBag.Value.Class = 2 + keyBag.Value.Tag = 0 + keyBag.Value.IsCompound = true + if keyBag.Value.Bytes, err = encodePkcs8ShroudedKeyBag(rand, privateKey, encodedPassword); err != nil { + return nil, err + } + keyBag.Attributes = append(keyBag.Attributes, localKeyIdAttr) + + // Construct an authenticated safe with two SafeContents. + // The first SafeContents is encrypted and contains the cert bags. + // The second SafeContents is unencrypted and contains the shrouded key bag. + var authenticatedSafe [2]contentInfo + if authenticatedSafe[0], err = makeSafeContents(rand, certBags, encodedPassword); err != nil { + return nil, err + } + if authenticatedSafe[1], err = makeSafeContents(rand, []safeBag{keyBag}, nil); err != nil { + return nil, err + } + + var authenticatedSafeBytes []byte + if authenticatedSafeBytes, err = asn1.Marshal(authenticatedSafe[:]); err != nil { + return nil, err + } + + // compute the MAC + pfx.MacData.Mac.Algorithm.Algorithm = oidSHA1 + pfx.MacData.MacSalt = make([]byte, 8) + if _, err = rand.Read(pfx.MacData.MacSalt); err != nil { + return nil, err + } + pfx.MacData.Iterations = 1 + if err = computeMac(&pfx.MacData, authenticatedSafeBytes, encodedPassword); err != nil { + return nil, err + } + + pfx.AuthSafe.ContentType = oidDataContentType + pfx.AuthSafe.Content.Class = 2 + pfx.AuthSafe.Content.Tag = 0 + pfx.AuthSafe.Content.IsCompound = true + if pfx.AuthSafe.Content.Bytes, err = asn1.Marshal(authenticatedSafeBytes); err != nil { + return nil, err + } + + if pfxData, err = asn1.Marshal(pfx); err != nil { + return nil, errors.New("pkcs12: error writing P12 data: " + err.Error()) + } + return +} + +func makeCertBag(certBytes []byte, attributes []pkcs12Attribute) (certBag *safeBag, err error) { + certBag = new(safeBag) + certBag.Id = oidCertBag + certBag.Value.Class = 2 + certBag.Value.Tag = 0 + certBag.Value.IsCompound = true + if certBag.Value.Bytes, err = encodeCertBag(certBytes); err != nil { + return nil, err + } + certBag.Attributes = attributes + return +} + +func makeSafeContents(rand io.Reader, bags []safeBag, password []byte) (ci contentInfo, err error) { + var data []byte + if data, err = asn1.Marshal(bags); err != nil { + return + } + + if password == nil { + ci.ContentType = oidDataContentType + ci.Content.Class = 2 + ci.Content.Tag = 0 + ci.Content.IsCompound = true + if ci.Content.Bytes, err = asn1.Marshal(data); err != nil { + return + } + } else { + randomSalt := make([]byte, 8) + if _, err = rand.Read(randomSalt); err != nil { + return + } + + var algo pkix.AlgorithmIdentifier + algo.Algorithm = oidPBEWithSHAAnd40BitRC2CBC + if algo.Parameters.FullBytes, err = asn1.Marshal(pbeParams{Salt: randomSalt, Iterations: 2048}); err != nil { + return + } + + var encryptedData encryptedData + encryptedData.Version = 0 + encryptedData.EncryptedContentInfo.ContentType = oidDataContentType + encryptedData.EncryptedContentInfo.ContentEncryptionAlgorithm = algo + if err = pbEncrypt(&encryptedData.EncryptedContentInfo, data, password); err != nil { + return + } + + ci.ContentType = oidEncryptedDataContentType + ci.Content.Class = 2 + ci.Content.Tag = 0 + ci.Content.IsCompound = true + if ci.Content.Bytes, err = asn1.Marshal(encryptedData); err != nil { + return + } + } + return +} diff --git a/vendor/github.com/bitrise-io/go-utils/pkcs12/safebags.go b/vendor/github.com/bitrise-io/go-utils/pkcs12/safebags.go new file mode 100644 index 0000000..be83a49 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/pkcs12/safebags.go @@ -0,0 +1,99 @@ +// Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "crypto/x509" + "encoding/asn1" + "errors" + "io" +) + +var ( + // see https://tools.ietf.org/html/rfc7292#appendix-D + oidCertTypeX509Certificate = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 22, 1}) + oidPKCS8ShroundedKeyBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 2}) + oidCertBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 3}) +) + +type certBag struct { + Id asn1.ObjectIdentifier + Data []byte `asn1:"tag:0,explicit"` +} + +func decodePkcs8ShroudedKeyBag(asn1Data, password []byte) (privateKey interface{}, err error) { + pkinfo := new(encryptedPrivateKeyInfo) + if err = unmarshal(asn1Data, pkinfo); err != nil { + return nil, errors.New("pkcs12: error decoding PKCS#8 shrouded key bag: " + err.Error()) + } + + pkData, err := pbDecrypt(pkinfo, password) + if err != nil { + return nil, errors.New("pkcs12: error decrypting PKCS#8 shrouded key bag: " + err.Error()) + } + + ret := new(asn1.RawValue) + if err = unmarshal(pkData, ret); err != nil { + return nil, errors.New("pkcs12: error unmarshaling decrypted private key: " + err.Error()) + } + + if privateKey, err = x509.ParsePKCS8PrivateKey(pkData); err != nil { + return nil, errors.New("pkcs12: error parsing PKCS#8 private key: " + err.Error()) + } + + return privateKey, nil +} + +func encodePkcs8ShroudedKeyBag(rand io.Reader, privateKey interface{}, password []byte) (asn1Data []byte, err error) { + var pkData []byte + if pkData, err = x509.MarshalPKCS8PrivateKey(privateKey); err != nil { + return nil, errors.New("pkcs12: error encoding PKCS#8 private key: " + err.Error()) + } + + randomSalt := make([]byte, 8) + if _, err = rand.Read(randomSalt); err != nil { + return nil, errors.New("pkcs12: error reading random salt: " + err.Error()) + } + var paramBytes []byte + if paramBytes, err = asn1.Marshal(pbeParams{Salt: randomSalt, Iterations: 2048}); err != nil { + return nil, errors.New("pkcs12: error encoding params: " + err.Error()) + } + + var pkinfo encryptedPrivateKeyInfo + pkinfo.AlgorithmIdentifier.Algorithm = oidPBEWithSHAAnd3KeyTripleDESCBC + pkinfo.AlgorithmIdentifier.Parameters.FullBytes = paramBytes + + if err = pbEncrypt(&pkinfo, pkData, password); err != nil { + return nil, errors.New("pkcs12: error encrypting PKCS#8 shrouded key bag: " + err.Error()) + } + + if asn1Data, err = asn1.Marshal(pkinfo); err != nil { + return nil, errors.New("pkcs12: error encoding PKCS#8 shrouded key bag: " + err.Error()) + } + + return asn1Data, nil +} + +func decodeCertBag(asn1Data []byte) (x509Certificates []byte, err error) { + bag := new(certBag) + if err := unmarshal(asn1Data, bag); err != nil { + return nil, errors.New("pkcs12: error decoding cert bag: " + err.Error()) + } + if !bag.Id.Equal(oidCertTypeX509Certificate) { + return nil, NotImplementedError("only X509 certificates are supported") + } + return bag.Data, nil +} + +func encodeCertBag(x509Certificates []byte) (asn1Data []byte, err error) { + var bag certBag + bag.Id = oidCertTypeX509Certificate + bag.Data = x509Certificates + if asn1Data, err = asn1.Marshal(bag); err != nil { + return nil, errors.New("pkcs12: error encoding cert bag: " + err.Error()) + } + return asn1Data, nil +} diff --git a/vendor/github.com/bitrise-io/go-utils/sliceutil/sliceutil.go b/vendor/github.com/bitrise-io/go-utils/sliceutil/sliceutil.go new file mode 100644 index 0000000..94b1a2c --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/sliceutil/sliceutil.go @@ -0,0 +1,46 @@ +package sliceutil + +import "strings" + +// UniqueStringSlice - returns a cleaned up list, +// where every item is unique. +// Does NOT guarantee any ordering, the result can +// be in any order! +func UniqueStringSlice(strs []string) []string { + lookupMap := map[string]interface{}{} + for _, aStr := range strs { + lookupMap[aStr] = 1 + } + uniqueStrs := []string{} + for k := range lookupMap { + uniqueStrs = append(uniqueStrs, k) + } + return uniqueStrs +} + +// IndexOfStringInSlice ... +func IndexOfStringInSlice(searchFor string, searchIn []string) int { + for idx, anItm := range searchIn { + if anItm == searchFor { + return idx + } + } + return -1 +} + +// IsStringInSlice ... +func IsStringInSlice(searchFor string, searchIn []string) bool { + return IndexOfStringInSlice(searchFor, searchIn) >= 0 +} + +// CleanWhitespace removes leading and trailing white space from each element of the input slice. +// Elements that end up as empty strings are excluded from the result depending on the value of the omitEmpty flag. +func CleanWhitespace(list []string, omitEmpty bool) (items []string) { + for _, e := range list { + e = strings.TrimSpace(e) + if !omitEmpty || len(e) > 0 { + items = append(items, e) + } + } + return +} diff --git a/vendor/github.com/bitrise-io/go-utils/stringutil/stringutil.go b/vendor/github.com/bitrise-io/go-utils/stringutil/stringutil.go new file mode 100644 index 0000000..5dde311 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/stringutil/stringutil.go @@ -0,0 +1,104 @@ +package stringutil + +import ( + "bufio" + "fmt" + "strings" +) + +// ReadFirstLine ... +func ReadFirstLine(s string, isIgnoreLeadingEmptyLines bool) string { + scanner := bufio.NewScanner(strings.NewReader(s)) + firstLine := "" + for scanner.Scan() { + firstLine = scanner.Text() + if !isIgnoreLeadingEmptyLines || firstLine != "" { + break + } + } + return firstLine +} + +// CaseInsensitiveEquals ... +func CaseInsensitiveEquals(a, b string) bool { + a, b = strings.ToLower(a), strings.ToLower(b) + return a == b +} + +// CaseInsensitiveContains ... +func CaseInsensitiveContains(s, substr string) bool { + s, substr = strings.ToLower(s), strings.ToLower(substr) + return strings.Contains(s, substr) +} + +// MaxLastChars returns the last maxCharCount characters, +// or in case maxCharCount is more than or equal to the string's length +// it'll just return the whole string. +func MaxLastChars(inStr string, maxCharCount int) string { + return genericTrim(inStr, maxCharCount, true, false) +} + +// MaxLastCharsWithDots ... +func MaxLastCharsWithDots(inStr string, maxCharCount int) string { + return genericTrim(inStr, maxCharCount, true, true) +} + +// MaxFirstChars ... +func MaxFirstChars(inStr string, maxCharCount int) string { + return genericTrim(inStr, maxCharCount, false, false) +} + +// MaxFirstCharsWithDots ... +func MaxFirstCharsWithDots(inStr string, maxCharCount int) string { + return genericTrim(inStr, maxCharCount, false, true) +} + +func genericTrim(inStr string, maxCharCount int, trimmAtStart, appendDots bool) string { + retStr := inStr + strLen := len(inStr) + + if maxCharCount >= strLen { + return retStr + } + + if appendDots { + if maxCharCount < 4 { + fmt.Println("Append dots mode, but string length < 4") + return "" + } + } + if trimmAtStart { + if appendDots { + retStr = inStr[strLen-(maxCharCount-3):] + } else { + retStr = inStr[strLen-maxCharCount:] + } + } else { + if appendDots { + retStr = inStr[:maxCharCount-3] + } else { + retStr = inStr[:maxCharCount] + } + } + + if appendDots { + if trimmAtStart { + retStr = "..." + retStr + } else { + retStr = retStr + "..." + } + } + return retStr +} + +// LastNLines ... +func LastNLines(s string, n int) string { + trimmed := strings.Trim(s, "\n") + splitted := strings.Split(trimmed, "\n") + + if len(splitted) >= n { + splitted = splitted[len(splitted)-n:] + } + + return strings.Join(splitted, "\n") +} diff --git a/vendor/github.com/bitrise-io/go-utils/stringutil/text.go b/vendor/github.com/bitrise-io/go-utils/stringutil/text.go new file mode 100644 index 0000000..49c0ed3 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/stringutil/text.go @@ -0,0 +1,49 @@ +package stringutil + +import ( + "bufio" + "math" + "strings" +) + +// IndentTextWithMaxLength ... +func IndentTextWithMaxLength(text, indent string, maxTextLineCharWidth int, isIndentFirstLine bool) string { + if maxTextLineCharWidth < 1 { + return "" + } + + formattedText := "" + + addLine := func(line string) { + isFirstLine := (formattedText == "") + if isFirstLine && !isIndentFirstLine { + formattedText = line + } else { + if !isFirstLine { + formattedText += "\n" + } + formattedText += indent + line + } + } + + scanner := bufio.NewScanner(strings.NewReader(text)) + for scanner.Scan() { + line := scanner.Text() + lineLength := len(line) + if lineLength > maxTextLineCharWidth { + lineCnt := math.Ceil(float64(lineLength) / float64(maxTextLineCharWidth)) + for i := 0; i < int(lineCnt); i++ { + startIdx := i * maxTextLineCharWidth + endIdx := startIdx + maxTextLineCharWidth + if endIdx > lineLength { + endIdx = lineLength + } + addLine(line[startIdx:endIdx]) + } + } else { + addLine(line) + } + } + + return formattedText +} diff --git a/vendor/github.com/bitrise-io/go-utils/ziputil/ziputil.go b/vendor/github.com/bitrise-io/go-utils/ziputil/ziputil.go new file mode 100644 index 0000000..29a6252 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/ziputil/ziputil.go @@ -0,0 +1,71 @@ +package ziputil + +import ( + "fmt" + "path/filepath" + + "github.com/bitrise-io/go-utils/command" + "github.com/bitrise-io/go-utils/pathutil" +) + +// ZipDir ... +func ZipDir(sourceDirPth, destinationZipPth string, isContentOnly bool) error { + if exist, err := pathutil.IsDirExists(sourceDirPth); err != nil { + return err + } else if !exist { + return fmt.Errorf("dir (%s) not exist", sourceDirPth) + } + + workDir := filepath.Dir(sourceDirPth) + if isContentOnly { + workDir = sourceDirPth + } + + zipTarget := filepath.Base(sourceDirPth) + if isContentOnly { + zipTarget = "." + } + + // -r - Travel the directory structure recursively + // -T - Test the integrity of the new zip file + // -y - Store symbolic links as such in the zip archive, instead of compressing and storing the file referred to by the link + cmd := command.New("/usr/bin/zip", "-rTy", destinationZipPth, zipTarget) + cmd.SetDir(workDir) + if out, err := cmd.RunAndReturnTrimmedCombinedOutput(); err != nil { + return fmt.Errorf("command: (%s) failed, output: %s, error: %s", cmd.PrintableCommandArgs(), out, err) + } + + return nil +} + +// ZipFile ... +func ZipFile(sourceFilePth, destinationZipPth string) error { + if exist, err := pathutil.IsPathExists(sourceFilePth); err != nil { + return err + } else if !exist { + return fmt.Errorf("file (%s) not exist", sourceFilePth) + } + + workDir := filepath.Dir(sourceFilePth) + zipTarget := filepath.Base(sourceFilePth) + + // -T - Test the integrity of the new zip file + // -y - Store symbolic links as such in the zip archive, instead of compressing and storing the file referred to by the link + cmd := command.New("/usr/bin/zip", "-Ty", destinationZipPth, zipTarget) + cmd.SetDir(workDir) + if out, err := cmd.RunAndReturnTrimmedCombinedOutput(); err != nil { + return fmt.Errorf("command: (%s) failed, output: %s, error: %s", cmd.PrintableCommandArgs(), out, err) + } + + return nil +} + +// UnZip ... +func UnZip(zip, intoDir string) error { + cmd := command.New("/usr/bin/unzip", zip, "-d", intoDir) + if out, err := cmd.RunAndReturnTrimmedCombinedOutput(); err != nil { + return fmt.Errorf("command: (%s) failed, output: %s, error: %s", cmd.PrintableCommandArgs(), out, err) + } + + return nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/certificateutil/filter.go b/vendor/github.com/bitrise-io/go-xcode/certificateutil/filter.go new file mode 100644 index 0000000..24fe025 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/certificateutil/filter.go @@ -0,0 +1,58 @@ +package certificateutil + +import "sort" + +// FilterCertificateInfoModelsByFilterFunc ... +func FilterCertificateInfoModelsByFilterFunc(certificates []CertificateInfoModel, filterFunc func(certificate CertificateInfoModel) bool) []CertificateInfoModel { + filteredCertificates := []CertificateInfoModel{} + + for _, certificate := range certificates { + if filterFunc(certificate) { + filteredCertificates = append(filteredCertificates, certificate) + } + } + + return filteredCertificates +} + +// ValidCertificateInfo contains the certificate infos filtered as valid, invalid and duplicated common name certificates +type ValidCertificateInfo struct { + ValidCertificates, + InvalidCertificates, + DuplicatedCertificates []CertificateInfoModel +} + +// FilterValidCertificateInfos filters out invalid and duplicated common name certificaates +func FilterValidCertificateInfos(certificateInfos []CertificateInfoModel) ValidCertificateInfo { + var invalidCertificates []CertificateInfoModel + nameToCerts := map[string][]CertificateInfoModel{} + for _, certificateInfo := range certificateInfos { + if certificateInfo.CheckValidity() != nil { + invalidCertificates = append(invalidCertificates, certificateInfo) + continue + } + + nameToCerts[certificateInfo.CommonName] = append(nameToCerts[certificateInfo.CommonName], certificateInfo) + } + + var validCertificates, duplicatedCertificates []CertificateInfoModel + for _, certs := range nameToCerts { + if len(certs) == 0 { + continue + } + + sort.Slice(certs, func(i, j int) bool { + return certs[i].EndDate.Before(certs[j].EndDate) + }) + validCertificates = append(validCertificates, certs[0]) + if len(certs) > 1 { + duplicatedCertificates = append(duplicatedCertificates, certs[1:]...) + } + } + + return ValidCertificateInfo{ + ValidCertificates: validCertificates, + InvalidCertificates: invalidCertificates, + DuplicatedCertificates: duplicatedCertificates, + } +} diff --git a/vendor/github.com/bitrise-io/go-xcode/certificateutil/info_model.go b/vendor/github.com/bitrise-io/go-xcode/certificateutil/info_model.go new file mode 100644 index 0000000..bf47896 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/certificateutil/info_model.go @@ -0,0 +1,119 @@ +package certificateutil + +import ( + "crypto/rand" + "crypto/sha1" + "crypto/x509" + "fmt" + "strings" + "time" + + "github.com/bitrise-io/go-utils/pkcs12" +) + +// CertificateInfoModel ... +type CertificateInfoModel struct { + CommonName string + TeamName string + TeamID string + EndDate time.Time + StartDate time.Time + + Serial string + SHA1Fingerprint string + + Certificate x509.Certificate + PrivateKey interface{} +} + +// String ... +func (info CertificateInfoModel) String() string { + team := fmt.Sprintf("%s (%s)", info.TeamName, info.TeamID) + certInfo := fmt.Sprintf("Serial: %s, Name: %s, Team: %s, Expiry: %s", info.Serial, info.CommonName, team, info.EndDate) + + err := info.CheckValidity() + if err != nil { + certInfo = certInfo + fmt.Sprintf(", error: %s", err) + } + + return certInfo +} + +// CheckValidity ... +func CheckValidity(certificate x509.Certificate) error { + timeNow := time.Now() + if !timeNow.After(certificate.NotBefore) { + return fmt.Errorf("Certificate is not yet valid - validity starts at: %s", certificate.NotBefore) + } + if !timeNow.Before(certificate.NotAfter) { + return fmt.Errorf("Certificate is not valid anymore - validity ended at: %s", certificate.NotAfter) + } + return nil +} + +// CheckValidity ... +func (info CertificateInfoModel) CheckValidity() error { + return CheckValidity(info.Certificate) +} + +// EncodeToP12 encodes a CertificateInfoModel in pkcs12 (.p12) format. +func (info CertificateInfoModel) EncodeToP12(passphrase string) ([]byte, error) { + return pkcs12.Encode(rand.Reader, info.PrivateKey, &info.Certificate, nil, passphrase) +} + +// NewCertificateInfo ... +func NewCertificateInfo(certificate x509.Certificate, privateKey interface{}) CertificateInfoModel { + fingerprint := sha1.Sum(certificate.Raw) + fingerprintStr := fmt.Sprintf("%x", fingerprint) + + return CertificateInfoModel{ + CommonName: certificate.Subject.CommonName, + TeamName: strings.Join(certificate.Subject.Organization, " "), + TeamID: strings.Join(certificate.Subject.OrganizationalUnit, " "), + EndDate: certificate.NotAfter, + StartDate: certificate.NotBefore, + Serial: certificate.SerialNumber.String(), + SHA1Fingerprint: fingerprintStr, + + Certificate: certificate, + PrivateKey: privateKey, + } +} + +// InstalledCodesigningCertificateInfos ... +func InstalledCodesigningCertificateInfos() ([]CertificateInfoModel, error) { + certificates, err := InstalledCodesigningCertificates() + if err != nil { + return nil, err + } + + infos := []CertificateInfoModel{} + for _, certificate := range certificates { + if certificate != nil { + infos = append(infos, NewCertificateInfo(*certificate, nil)) + } + } + + return infos, nil +} + +// InstalledInstallerCertificateInfos ... +func InstalledInstallerCertificateInfos() ([]CertificateInfoModel, error) { + certificates, err := InstalledMacAppStoreCertificates() + if err != nil { + return nil, err + } + + infos := []CertificateInfoModel{} + for _, certificate := range certificates { + if certificate != nil { + infos = append(infos, NewCertificateInfo(*certificate, nil)) + } + } + + installerCertificates := FilterCertificateInfoModelsByFilterFunc(infos, func(cert CertificateInfoModel) bool { + return strings.Contains(cert.CommonName, "Installer") + }) + + return installerCertificates, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/certificateutil/test_util.go b/vendor/github.com/bitrise-io/go-xcode/certificateutil/test_util.go new file mode 100644 index 0000000..e5e54d6 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/certificateutil/test_util.go @@ -0,0 +1,80 @@ +package certificateutil + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "math/big" + "time" +) + +// GenerateTestCertificate creates a certificate (signed by a self-signed CA cert) for test purposes +func GenerateTestCertificate(serial int64, teamID, teamName, commonName string, expiry time.Time) (*x509.Certificate, *rsa.PrivateKey, error) { + CAtemplate := &x509.Certificate{ + IsCA: true, + BasicConstraintsValid: true, + SubjectKeyId: []byte{1, 2, 3}, + SerialNumber: big.NewInt(1234), + Subject: pkix.Name{ + Country: []string{"US"}, + Organization: []string{"Pear Worldwide Developer Relations"}, + CommonName: "Pear Worldwide Developer Relations CA", + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(1, 0, 0), + // see http://golang.org/pkg/crypto/x509/#KeyUsage + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + } + + // generate private key + privatekey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, nil, err + } + + // Self-signed certificate, parent is the template + CAcertData, err := x509.CreateCertificate(rand.Reader, CAtemplate, CAtemplate, &privatekey.PublicKey, privatekey) + if err != nil { + return nil, nil, err + } + CAcert, err := x509.ParseCertificate(CAcertData) + if err != nil { + return nil, nil, err + } + + template := &x509.Certificate{ + IsCA: true, + BasicConstraintsValid: true, + SerialNumber: big.NewInt(serial), + Subject: pkix.Name{ + Country: []string{"US"}, + Organization: []string{teamName}, + OrganizationalUnit: []string{teamID}, + CommonName: commonName, + }, + NotBefore: time.Now(), + NotAfter: expiry, + // see http://golang.org/pkg/crypto/x509/#KeyUsage + KeyUsage: x509.KeyUsageDigitalSignature, + } + + // generate private key + privatekey, err = rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, nil, err + } + + certData, err := x509.CreateCertificate(rand.Reader, template, CAcert, &privatekey.PublicKey, privatekey) + if err != nil { + return nil, nil, err + } + + cert, err := x509.ParseCertificate(certData) + if err != nil { + return nil, nil, err + } + + return cert, privatekey, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/certificateutil/util.go b/vendor/github.com/bitrise-io/go-xcode/certificateutil/util.go new file mode 100644 index 0000000..4fc9691 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/certificateutil/util.go @@ -0,0 +1,179 @@ +package certificateutil + +import ( + "bufio" + "crypto/x509" + "encoding/pem" + "fmt" + "regexp" + "strings" + + "github.com/bitrise-io/go-utils/command" + "github.com/bitrise-io/go-utils/fileutil" + "github.com/bitrise-io/go-utils/pkcs12" + "github.com/pkg/errors" +) + +func commandError(printableCmd string, cmdOut string, cmdErr error) error { + return errors.Wrapf(cmdErr, "%s failed, out: %s", printableCmd, cmdOut) +} + +// CertificatesFromPKCS12Content returns an array of CertificateInfoModel +// Used to parse p12 file containing multiple codesign identities (exported from macOS Keychain) +func CertificatesFromPKCS12Content(content []byte, password string) ([]CertificateInfoModel, error) { + certificates, privateKeys, err := pkcs12.DecodeAll(content, password) + if err != nil { + return nil, err + } + + if len(certificates) != len(privateKeys) { + return nil, errors.New("pkcs12: different number of certificates and private keys found") + } + + if len(certificates) == 0 { + return nil, errors.New("pkcs12: no certificate and private key pair found") + } + + infos := []CertificateInfoModel{} + for i, certificate := range certificates { + if certificate != nil { + infos = append(infos, NewCertificateInfo(*certificate, privateKeys[i])) + } + } + + return infos, nil +} + +// CertificatesFromPKCS12File ... +func CertificatesFromPKCS12File(pkcs12Pth, password string) ([]CertificateInfoModel, error) { + content, err := fileutil.ReadBytesFromFile(pkcs12Pth) + if err != nil { + return nil, err + } + + return CertificatesFromPKCS12Content(content, password) +} + +// CertificateFromDERContent ... +func CertificateFromDERContent(content []byte) (*x509.Certificate, error) { + return x509.ParseCertificate(content) +} + +// CeritifcateFromPemContent ... +func CeritifcateFromPemContent(content []byte) (*x509.Certificate, error) { + block, _ := pem.Decode(content) + if block == nil || block.Bytes == nil || len(block.Bytes) == 0 { + return nil, fmt.Errorf("failed to parse profile from: %s", string(content)) + } + return CertificateFromDERContent(block.Bytes) +} + +func installedCodesigningCertificateNamesFromOutput(out string) ([]string, error) { + pettern := `^[0-9]+\) (?P.*) "(?P.*)"` + re := regexp.MustCompile(pettern) + + certificateNameMap := map[string]bool{} + scanner := bufio.NewScanner(strings.NewReader(out)) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if matches := re.FindStringSubmatch(line); len(matches) == 3 { + name := matches[2] + certificateNameMap[name] = true + } + } + if err := scanner.Err(); err != nil { + return nil, err + } + + names := []string{} + for name := range certificateNameMap { + names = append(names, name) + } + return names, nil +} + +// InstalledCodesigningCertificateNames ... +func InstalledCodesigningCertificateNames() ([]string, error) { + cmd := command.New("security", "find-identity", "-v", "-p", "codesigning") + out, err := cmd.RunAndReturnTrimmedCombinedOutput() + if err != nil { + return nil, commandError(cmd.PrintableCommandArgs(), out, err) + } + return installedCodesigningCertificateNamesFromOutput(out) +} + +// InstalledMacAppStoreCertificateNames ... +func InstalledMacAppStoreCertificateNames() ([]string, error) { + cmd := command.New("security", "find-identity", "-v", "-p", "macappstore") + out, err := cmd.RunAndReturnTrimmedCombinedOutput() + if err != nil { + return nil, commandError(cmd.PrintableCommandArgs(), out, err) + } + return installedCodesigningCertificateNamesFromOutput(out) +} + +func normalizeFindCertificateOut(out string) ([]string, error) { + certificateContents := []string{} + pattern := `(?s)(-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----)` + matches := regexp.MustCompile(pattern).FindAllString(out, -1) + if len(matches) == 0 { + return nil, fmt.Errorf("no certificates found in: %s", out) + } + + for _, certificateContent := range matches { + if !strings.HasPrefix(certificateContent, "\n") { + certificateContent = "\n" + certificateContent + } + if !strings.HasSuffix(certificateContent, "\n") { + certificateContent = certificateContent + "\n" + } + certificateContents = append(certificateContents, certificateContent) + } + + return certificateContents, nil +} + +// InstalledCodesigningCertificates ... +func InstalledCodesigningCertificates() ([]*x509.Certificate, error) { + certificateNames, err := InstalledCodesigningCertificateNames() + if err != nil { + return nil, err + } + return getInstalledCertificatesByNameSlice(certificateNames) +} + +// InstalledMacAppStoreCertificates ... +func InstalledMacAppStoreCertificates() ([]*x509.Certificate, error) { + certificateNames, err := InstalledMacAppStoreCertificateNames() + if err != nil { + return nil, err + } + return getInstalledCertificatesByNameSlice(certificateNames) +} + +func getInstalledCertificatesByNameSlice(certificateNames []string) ([]*x509.Certificate, error) { + certificates := []*x509.Certificate{} + for _, name := range certificateNames { + cmd := command.New("security", "find-certificate", "-c", name, "-p", "-a") + out, err := cmd.RunAndReturnTrimmedCombinedOutput() + if err != nil { + return nil, commandError(cmd.PrintableCommandArgs(), out, err) + } + + normalizedOuts, err := normalizeFindCertificateOut(out) + if err != nil { + return nil, err + } + + for _, normalizedOut := range normalizedOuts { + certificate, err := CeritifcateFromPemContent([]byte(normalizedOut)) + if err != nil { + return nil, err + } + + certificates = append(certificates, certificate) + } + } + + return certificates, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/exportoptions/appstore_options.go b/vendor/github.com/bitrise-io/go-xcode/exportoptions/appstore_options.go new file mode 100644 index 0000000..413cbe9 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/exportoptions/appstore_options.go @@ -0,0 +1,80 @@ +package exportoptions + +import ( + "fmt" + + "howett.net/plist" +) + +// AppStoreOptionsModel ... +type AppStoreOptionsModel struct { + TeamID string + BundleIDProvisioningProfileMapping map[string]string + SigningCertificate string + InstallerSigningCertificate string + SigningStyle string + ICloudContainerEnvironment ICloudContainerEnvironment + + // for app-store exports + UploadBitcode bool + UploadSymbols bool +} + +// NewAppStoreOptions ... +func NewAppStoreOptions() AppStoreOptionsModel { + return AppStoreOptionsModel{ + UploadBitcode: UploadBitcodeDefault, + UploadSymbols: UploadSymbolsDefault, + } +} + +// Hash ... +func (options AppStoreOptionsModel) Hash() map[string]interface{} { + hash := map[string]interface{}{} + hash[MethodKey] = MethodAppStore + if options.TeamID != "" { + hash[TeamIDKey] = options.TeamID + } + if options.UploadBitcode != UploadBitcodeDefault { + hash[UploadBitcodeKey] = options.UploadBitcode + } + if options.UploadSymbols != UploadSymbolsDefault { + hash[UploadSymbolsKey] = options.UploadSymbols + } + if options.ICloudContainerEnvironment != "" { + hash[ICloudContainerEnvironmentKey] = options.ICloudContainerEnvironment + } + if len(options.BundleIDProvisioningProfileMapping) > 0 { + hash[ProvisioningProfilesKey] = options.BundleIDProvisioningProfileMapping + } + if options.SigningCertificate != "" { + hash[SigningCertificateKey] = options.SigningCertificate + } + if options.InstallerSigningCertificate != "" { + hash[InstallerSigningCertificateKey] = options.InstallerSigningCertificate + } + if options.SigningStyle != "" { + hash[SigningStyleKey] = options.SigningStyle + } + return hash +} + +// String ... +func (options AppStoreOptionsModel) String() (string, error) { + hash := options.Hash() + plistBytes, err := plist.MarshalIndent(hash, plist.XMLFormat, "\t") + if err != nil { + return "", fmt.Errorf("failed to marshal export options model, error: %s", err) + } + return string(plistBytes), err +} + +// WriteToFile ... +func (options AppStoreOptionsModel) WriteToFile(pth string) error { + return WritePlistToFile(options.Hash(), pth) +} + +// WriteToTmpFile ... +func (options AppStoreOptionsModel) WriteToTmpFile() (string, error) { + return WritePlistToTmpFile(options.Hash()) +} diff --git a/vendor/github.com/bitrise-io/go-xcode/exportoptions/exportoptions.go b/vendor/github.com/bitrise-io/go-xcode/exportoptions/exportoptions.go new file mode 100644 index 0000000..7e0cdf9 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/exportoptions/exportoptions.go @@ -0,0 +1,46 @@ +package exportoptions + +import ( + "fmt" + "path/filepath" + + "github.com/bitrise-io/go-utils/fileutil" + "github.com/bitrise-io/go-utils/pathutil" + "howett.net/plist" +) + +// ExportOptions ... +type ExportOptions interface { + Hash() map[string]interface{} + String() (string, error) + WriteToFile(pth string) error + WriteToTmpFile() (string, error) +} + +// WritePlistToFile ... +func WritePlistToFile(options map[string]interface{}, pth string) error { + plistBytes, err := plist.MarshalIndent(options, plist.XMLFormat, "\t") + if err != nil { + return fmt.Errorf("failed to marshal export options model, error: %s", err) + } + if err := fileutil.WriteBytesToFile(pth, plistBytes); err != nil { + return fmt.Errorf("failed to write export options, error: %s", err) + } + + return nil +} + +// WritePlistToTmpFile ... +func WritePlistToTmpFile(options map[string]interface{}) (string, error) { + tmpDir, err := pathutil.NormalizedOSTempDirPath("output") + if err != nil { + return "", fmt.Errorf("failed to create temp dir, error: %s", err) + } + pth := filepath.Join(tmpDir, "exportOptions.plist") + + if err := WritePlistToFile(options, pth); err != nil { + return "", fmt.Errorf("failed to write to file options, error: %s", err) + } + + return pth, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/exportoptions/non_appstore_options.go b/vendor/github.com/bitrise-io/go-xcode/exportoptions/non_appstore_options.go new file mode 100644 index 0000000..9eaa70f --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/exportoptions/non_appstore_options.go @@ -0,0 +1,93 @@ +package exportoptions + +import ( + "fmt" + + "howett.net/plist" +) + +// NonAppStoreOptionsModel ... +type NonAppStoreOptionsModel struct { + Method Method + TeamID string + BundleIDProvisioningProfileMapping map[string]string + SigningCertificate string + SigningStyle string + ICloudContainerEnvironment ICloudContainerEnvironment + + // for non app-store exports + CompileBitcode bool + EmbedOnDemandResourcesAssetPacksInBundle bool + Manifest Manifest + OnDemandResourcesAssetPacksBaseURL string + Thinning string +} + +// NewNonAppStoreOptions ... +func NewNonAppStoreOptions(method Method) NonAppStoreOptionsModel { + return NonAppStoreOptionsModel{ + Method: method, + CompileBitcode: CompileBitcodeDefault, + EmbedOnDemandResourcesAssetPacksInBundle: EmbedOnDemandResourcesAssetPacksInBundleDefault, + Thinning: ThinningDefault, + } +} + +// Hash ... +func (options NonAppStoreOptionsModel) Hash() map[string]interface{} { + hash := map[string]interface{}{} + if options.Method != "" { + hash[MethodKey] = options.Method + } + if options.TeamID != "" { + hash[TeamIDKey] = options.TeamID + } + if options.CompileBitcode != CompileBitcodeDefault { + hash[CompileBitcodeKey] = options.CompileBitcode + } + if options.EmbedOnDemandResourcesAssetPacksInBundle != EmbedOnDemandResourcesAssetPacksInBundleDefault { + hash[EmbedOnDemandResourcesAssetPacksInBundleKey] = options.EmbedOnDemandResourcesAssetPacksInBundle + } + if options.ICloudContainerEnvironment != "" { + hash[ICloudContainerEnvironmentKey] = options.ICloudContainerEnvironment + } + if !options.Manifest.IsEmpty() { + hash[ManifestKey] = options.Manifest.ToHash() + } + if options.OnDemandResourcesAssetPacksBaseURL != "" { + hash[OnDemandResourcesAssetPacksBaseURLKey] = options.OnDemandResourcesAssetPacksBaseURL + } + if options.Thinning != ThinningDefault { + hash[ThinningKey] = options.Thinning + } + if len(options.BundleIDProvisioningProfileMapping) > 0 { + hash[ProvisioningProfilesKey] = options.BundleIDProvisioningProfileMapping + } + if options.SigningCertificate != "" { + hash[SigningCertificateKey] = options.SigningCertificate + } + if options.SigningStyle != "" { + hash[SigningStyleKey] = options.SigningStyle + } + return hash +} + +// String ... +func (options NonAppStoreOptionsModel) String() (string, error) { + hash := options.Hash() + plistBytes, err := plist.MarshalIndent(hash, plist.XMLFormat, "\t") + if err != nil { + return "", fmt.Errorf("failed to marshal export options model, error: %s", err) + } + return string(plistBytes), err +} + +// WriteToFile ... +func (options NonAppStoreOptionsModel) WriteToFile(pth string) error { + return WritePlistToFile(options.Hash(), pth) +} + +// WriteToTmpFile ... +func (options NonAppStoreOptionsModel) WriteToTmpFile() (string, error) { + return WritePlistToTmpFile(options.Hash()) +} diff --git a/vendor/github.com/bitrise-io/go-xcode/exportoptions/properties.go b/vendor/github.com/bitrise-io/go-xcode/exportoptions/properties.go new file mode 100644 index 0000000..7ab2a90 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/exportoptions/properties.go @@ -0,0 +1,159 @@ +package exportoptions + +import "fmt" + +// CompileBitcodeKey ... +const CompileBitcodeKey = "compileBitcode" + +// CompileBitcodeDefault ... +const CompileBitcodeDefault = true + +// EmbedOnDemandResourcesAssetPacksInBundleKey ... +const EmbedOnDemandResourcesAssetPacksInBundleKey = "embedOnDemandResourcesAssetPacksInBundle" + +// EmbedOnDemandResourcesAssetPacksInBundleDefault ... +const EmbedOnDemandResourcesAssetPacksInBundleDefault = true + +// ICloudContainerEnvironmentKey ... +const ICloudContainerEnvironmentKey = "iCloudContainerEnvironment" + +const ( + // ICloudContainerEnvironmentDevelopment ... + ICloudContainerEnvironmentDevelopment ICloudContainerEnvironment = "Development" + // ICloudContainerEnvironmentProduction ... + ICloudContainerEnvironmentProduction ICloudContainerEnvironment = "Production" +) + +// ICloudContainerEnvironment ... +type ICloudContainerEnvironment string + +// ManifestKey ... +const ManifestKey = "manifest" + +// ManifestAppURLKey ... +const ManifestAppURLKey = "appURL" + +// ManifestDisplayImageURLKey ... +const ManifestDisplayImageURLKey = "displayImageURL" + +// ManifestFullSizeImageURLKey ... +const ManifestFullSizeImageURLKey = "fullSizeImageURL" + +// ManifestAssetPackManifestURLKey ... +const ManifestAssetPackManifestURLKey = "assetPackManifestURL" + +// Manifest ... +type Manifest struct { + AppURL string + DisplayImageURL string + FullSizeImageURL string + AssetPackManifestURL string +} + +// IsEmpty ... +func (manifest Manifest) IsEmpty() bool { + return (manifest.AppURL == "" && manifest.DisplayImageURL == "" && manifest.FullSizeImageURL == "" && manifest.AssetPackManifestURL == "") +} + +// ToHash ... +func (manifest Manifest) ToHash() map[string]string { + hash := map[string]string{} + if manifest.AppURL != "" { + hash[ManifestAppURLKey] = manifest.AppURL + } + if manifest.DisplayImageURL != "" { + hash[ManifestDisplayImageURLKey] = manifest.DisplayImageURL + } + if manifest.FullSizeImageURL != "" { + hash[ManifestFullSizeImageURLKey] = manifest.FullSizeImageURL + } + if manifest.AssetPackManifestURL != "" { + hash[ManifestAssetPackManifestURLKey] = manifest.AssetPackManifestURL + } + return hash +} + +// MethodKey ... +const MethodKey = "method" + +const ( + // MethodAppStore ... + MethodAppStore Method = "app-store" + // MethodAdHoc ... + MethodAdHoc Method = "ad-hoc" + // MethodPackage ... + MethodPackage Method = "package" + // MethodEnterprise ... + MethodEnterprise Method = "enterprise" + // MethodDevelopment ... + MethodDevelopment Method = "development" + // MethodDeveloperID ... + MethodDeveloperID Method = "developer-id" + // MethodDefault ... + MethodDefault Method = MethodDevelopment +) + +// Method ... +type Method string + +// ParseMethod ... +func ParseMethod(method string) (Method, error) { + switch method { + case "app-store": + return MethodAppStore, nil + case "ad-hoc": + return MethodAdHoc, nil + case "package": + return MethodPackage, nil + case "enterprise": + return MethodEnterprise, nil + case "development": + return MethodDevelopment, nil + case "developer-id": + return MethodDeveloperID, nil + default: + return Method(""), fmt.Errorf("unkown method (%s)", method) + } +} + +// OnDemandResourcesAssetPacksBaseURLKey .... +const OnDemandResourcesAssetPacksBaseURLKey = "onDemandResourcesAssetPacksBaseURL" + +// TeamIDKey ... +const TeamIDKey = "teamID" + +// ThinningKey ... +const ThinningKey = "thinning" + +const ( + // ThinningNone ... + ThinningNone = "none" + // ThinningThinForAllVariants ... + ThinningThinForAllVariants = "thin-for-all-variants" + // ThinningDefault ... + ThinningDefault = ThinningNone +) + +// UploadBitcodeKey .... +const UploadBitcodeKey = "uploadBitcode" + +// UploadBitcodeDefault ... +const UploadBitcodeDefault = true + +// UploadSymbolsKey ... +const UploadSymbolsKey = "uploadSymbols" + +// UploadSymbolsDefault ... +const UploadSymbolsDefault = true + +// ProvisioningProfilesKey ... +const ProvisioningProfilesKey = "provisioningProfiles" + +// SigningCertificateKey ... +const SigningCertificateKey = "signingCertificate" + +// InstallerSigningCertificateKey ... +const InstallerSigningCertificateKey = "installerSigningCertificate" + +// SigningStyleKey ... +const SigningStyleKey = "signingStyle" diff --git a/vendor/github.com/bitrise-io/go-xcode/models/models.go b/vendor/github.com/bitrise-io/go-xcode/models/models.go new file mode 100644 index 0000000..42ea372 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/models/models.go @@ -0,0 +1,8 @@ +package models + +// XcodebuildVersionModel ... +type XcodebuildVersionModel struct { + Version string + BuildVersion string + MajorVersion int64 +} diff --git a/vendor/github.com/bitrise-io/go-xcode/plistutil/plistutil.go b/vendor/github.com/bitrise-io/go-xcode/plistutil/plistutil.go new file mode 100644 index 0000000..39691ca --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/plistutil/plistutil.go @@ -0,0 +1,230 @@ +package plistutil + +import ( + "errors" + "time" + + "github.com/bitrise-io/go-utils/fileutil" + "howett.net/plist" +) + +// PlistData ... +type PlistData map[string]interface{} + +// NewPlistDataFromContent ... +func NewPlistDataFromContent(plistContent string) (PlistData, error) { + var data PlistData + if _, err := plist.Unmarshal([]byte(plistContent), &data); err != nil { + return PlistData{}, err + } + return data, nil +} + +// NewPlistDataFromFile ... +func NewPlistDataFromFile(plistPth string) (PlistData, error) { + content, err := fileutil.ReadStringFromFile(plistPth) + if err != nil { + return PlistData{}, err + } + return NewPlistDataFromContent(content) +} + +// GetString ... +func (data PlistData) GetString(forKey string) (string, bool) { + value, ok := data[forKey] + if !ok { + return "", false + } + + casted, ok := value.(string) + if !ok { + return "", false + } + + return casted, true +} + +// GetUInt64 ... +func (data PlistData) GetUInt64(forKey string) (uint64, bool) { + value, ok := data[forKey] + if !ok { + return 0, false + } + + casted, ok := value.(uint64) + if !ok { + return 0, false + } + return casted, true +} + +// GetFloat64 ... +func (data PlistData) GetFloat64(forKey string) (float64, bool) { + value, ok := data[forKey] + if !ok { + return 0, false + } + + casted, ok := value.(float64) + if !ok { + return 0, false + } + return casted, true +} + +// GetBool ... +func (data PlistData) GetBool(forKey string) (bool, bool) { + value, ok := data[forKey] + if !ok { + return false, false + } + + casted, ok := value.(bool) + if !ok { + return false, false + } + + return casted, true +} + +// GetTime ... +func (data PlistData) GetTime(forKey string) (time.Time, bool) { + value, ok := data[forKey] + if !ok { + return time.Time{}, false + } + + casted, ok := value.(time.Time) + if !ok { + return time.Time{}, false + } + return casted, true +} + +// GetUInt64Array ... +func (data PlistData) GetUInt64Array(forKey string) ([]uint64, bool) { + value, ok := data[forKey] + if !ok { + return nil, false + } + + if casted, ok := value.([]uint64); ok { + return casted, true + } + + casted, ok := value.([]interface{}) + if !ok { + return nil, false + } + + array := []uint64{} + for _, v := range casted { + casted, ok := v.(uint64) + if !ok { + return nil, false + } + + array = append(array, casted) + } + return array, true +} + +// GetStringArray ... +func (data PlistData) GetStringArray(forKey string) ([]string, bool) { + value, ok := data[forKey] + if !ok { + return nil, false + } + + if casted, ok := value.([]string); ok { + return casted, true + } + + casted, ok := value.([]interface{}) + if !ok { + return nil, false + } + + array := []string{} + for _, v := range casted { + casted, ok := v.(string) + if !ok { + return nil, false + } + + array = append(array, casted) + } + return array, true +} + +// GetByteArrayArray ... +func (data PlistData) GetByteArrayArray(forKey string) ([][]byte, bool) { + value, ok := data[forKey] + if !ok { + return nil, false + } + + if casted, ok := value.([][]byte); ok { + return casted, true + } + + casted, ok := value.([]interface{}) + if !ok { + return nil, false + } + + array := [][]byte{} + for _, v := range casted { + casted, ok := v.([]byte) + if !ok { + return nil, false + } + + array = append(array, casted) + } + return array, true +} + +// GetMapStringInterface ... +func (data PlistData) GetMapStringInterface(forKey string) (PlistData, bool) { + value, ok := data[forKey] + if !ok { + return nil, false + } + + if casted, ok := value.(map[string]interface{}); ok { + return casted, true + } + return nil, false +} + +func castToMapStringInterfaceArray(obj interface{}) ([]PlistData, error) { + array, ok := obj.([]interface{}) + if !ok { + return nil, errors.New("failed to cast to []interface{}") + } + + var casted []PlistData + for _, item := range array { + mapStringInterface, ok := item.(map[string]interface{}) + if !ok { + return nil, errors.New("failed to cast to map[string]interface{}") + } + casted = append(casted, mapStringInterface) + } + + return casted, nil +} + +// GetMapStringInterfaceArray ... +func (data PlistData) GetMapStringInterfaceArray(forKey string) ([]PlistData, bool) { + value, ok := data[forKey] + if !ok { + return nil, false + } + mapStringInterfaceArray, err := castToMapStringInterfaceArray(value) + if err != nil { + return nil, false + } + return mapStringInterfaceArray, true +} diff --git a/vendor/github.com/bitrise-io/go-xcode/plistutil/plistutil_test_file_content.go b/vendor/github.com/bitrise-io/go-xcode/plistutil/plistutil_test_file_content.go new file mode 100644 index 0000000..17f1503 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/plistutil/plistutil_test_file_content.go @@ -0,0 +1,331 @@ +package plistutil + +const infoPlistContent = ` + + + + CFBundleName + ios-simple-objc + DTXcode + 0832 + DTSDKName + iphoneos10.3 + UILaunchStoryboardName + LaunchScreen + DTSDKBuild + 14E269 + CFBundleDevelopmentRegion + en + CFBundleVersion + 1 + BuildMachineOSBuild + 16F73 + DTPlatformName + iphoneos + CFBundlePackageType + APPL + UIMainStoryboardFile + Main + CFBundleSupportedPlatforms + + iPhoneOS + + CFBundleShortVersionString + 1.0 + CFBundleInfoDictionaryVersion + 6.0 + UIRequiredDeviceCapabilities + + armv7 + + CFBundleExecutable + ios-simple-objc + DTCompiler + com.apple.compilers.llvm.clang.1_0 + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CFBundleIdentifier + Bitrise.ios-simple-objc + MinimumOSVersion + 8.1 + DTXcodeBuild + 8E2002 + DTPlatformVersion + 10.3 + LSRequiresIPhoneOS + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CFBundleSignature + ???? + UIDeviceFamily + + 1 + 2 + + DTPlatformBuild + 14E269 + + +` + +const developmentProfileContent = ` + + + + AppIDName + Bitrise Test + ApplicationIdentifierPrefix + + 9NS4 + + CreationDate + 2016-09-22T11:28:46Z + Platform + + iOS + + DeveloperCertificates + + + + Entitlements + + keychain-access-groups + + 9NS4.* + + get-task-allow + + application-identifier + 9NS4.* + com.apple.developer.team-identifier + 9NS4 + + ExpirationDate + 2017-09-22T11:28:46Z + Name + Bitrise Test Development + ProvisionedDevices + + b138 + + TeamIdentifier + + 9NS4 + + TeamName + Some Dude + TimeToLive + 365 + UUID + 4b617a5f + Version + 1 +` + +const appStoreProfileContent = ` + + + + AppIDName + Bitrise Test + ApplicationIdentifierPrefix + + 9NS4 + + CreationDate + 2016-09-22T11:29:12Z + Platform + + iOS + + DeveloperCertificates + + + + Entitlements + + keychain-access-groups + + 9NS4.* + + get-task-allow + + application-identifier + 9NS4.* + com.apple.developer.team-identifier + 9NS4 + beta-reports-active + + + ExpirationDate + 2017-09-21T13:20:06Z + Name + Bitrise Test App Store + TeamIdentifier + + 9NS4 + + TeamName + Some Dude + TimeToLive + 364 + UUID + a60668dd + Version + 1 +` + +const adHocProfileContent = ` + + + + AppIDName + Bitrise Test + ApplicationIdentifierPrefix + + 9NS4 + + CreationDate + 2016-09-22T11:29:38Z + Platform + + iOS + + DeveloperCertificates + + + + Entitlements + + keychain-access-groups + + 9NS4.* + + get-task-allow + + application-identifier + 9NS4.* + com.apple.developer.team-identifier + 9NS4 + + ExpirationDate + 2017-09-21T13:20:06Z + Name + Bitrise Test Ad Hoc + ProvisionedDevices + + b138 + + TeamIdentifier + + 9NS4 + + TeamName + Some Dude + TimeToLive + 364 + UUID + 26668300 + Version + 1 +` + +const enterpriseProfileContent = ` + + + + AppIDName + Bitrise Test + ApplicationIdentifierPrefix + + PF3BP78LQ8 + + CreationDate + 2015-10-05T13:32:46Z + Platform + + iOS + + DeveloperCertificates + + + + Entitlements + + keychain-access-groups + + PF3BP78LQ8.* + + get-task-allow + + application-identifier + 9NS4.* + com.apple.developer.team-identifier + 9NS4 + + ExpirationDate + 2016-10-04T13:32:46Z + Name + Bitrise Test Enterprise + ProvisionsAllDevices + + TeamIdentifier + + PF3BP78LQ8 + + TeamName + Some Dude + TimeToLive + 365 + UUID + 8d6caa15 + Version + 1 +` + +const paritalTestSummariesContent = ` + + + + Duration + 0.34774100780487061 + Subtests + + + TestIdentifier + ios_simple_objcTests/testExample + TestStatus + Success + + + TestIdentifier + ios_simple_objcTests/testExample2 + TestStatus + Success + + + TestIdentifier + ios_simple_objcTests + TestName + ios_simple_objcTests + TestObjectClass + IDESchemeActionTestSummaryGroup + +TestIdentifier +ios-simple-objcTests.xctest +TestName +ios-simple-objcTests.xctest +TestObjectClass +IDESchemeActionTestSummaryGroup +` diff --git a/vendor/github.com/bitrise-io/go-xcode/profileutil/capabilities.go b/vendor/github.com/bitrise-io/go-xcode/profileutil/capabilities.go new file mode 100644 index 0000000..7bc709b --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/profileutil/capabilities.go @@ -0,0 +1,63 @@ +package profileutil + +import ( + "github.com/bitrise-io/go-xcode/plistutil" +) + +// MatchTargetAndProfileEntitlements ... +func MatchTargetAndProfileEntitlements(targetEntitlements plistutil.PlistData, profileEntitlements plistutil.PlistData, profileType ProfileType) []string { + missingEntitlements := []string{} + + for key := range targetEntitlements { + _, known := KnownProfileCapabilitiesMap[profileType][key] + if !known { + continue + } + _, found := profileEntitlements[key] + if !found { + missingEntitlements = append(missingEntitlements, key) + } + } + + return missingEntitlements +} + +// KnownProfileCapabilitiesMap ... +var KnownProfileCapabilitiesMap = map[ProfileType]map[string]bool{ + ProfileTypeMacOs: map[string]bool{ + "com.apple.developer.networking.networkextension": true, + "com.apple.developer.icloud-container-environment": true, + "com.apple.developer.icloud-container-development-container-identifiers": true, + "com.apple.developer.aps-environment": true, + "keychain-access-groups": true, + "com.apple.developer.icloud-services": true, + "com.apple.developer.icloud-container-identifiers": true, + "com.apple.developer.networking.vpn.api": true, + "com.apple.developer.ubiquity-kvstore-identifier": true, + "com.apple.developer.ubiquity-container-identifiers": true, + "com.apple.developer.game-center": true, + "com.apple.application-identifier": true, + "com.apple.developer.team-identifier": true, + "com.apple.developer.maps": true, + }, + ProfileTypeIos: map[string]bool{ + "com.apple.developer.in-app-payments": true, + "com.apple.security.application-groups": true, + "com.apple.developer.default-data-protection": true, + "com.apple.developer.healthkit": true, + "com.apple.developer.homekit": true, + "com.apple.developer.networking.HotspotConfiguration": true, + "inter-app-audio": true, + "keychain-access-groups": true, + "com.apple.developer.networking.multipath": true, + "com.apple.developer.nfc.readersession.formats": true, + "com.apple.developer.networking.networkextension": true, + "aps-environment": true, + "com.apple.developer.associated-domains": true, + "com.apple.developer.siri": true, + "com.apple.developer.networking.vpn.api": true, + "com.apple.external-accessory.wireless-configuration": true, + "com.apple.developer.pass-type-identifiers": true, + "com.apple.developer.icloud-container-identifiers": true, + }, +} diff --git a/vendor/github.com/bitrise-io/go-xcode/profileutil/info_model.go b/vendor/github.com/bitrise-io/go-xcode/profileutil/info_model.go new file mode 100644 index 0000000..3df1460 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/profileutil/info_model.go @@ -0,0 +1,227 @@ +package profileutil + +import ( + "crypto/x509" + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-io/go-xcode/certificateutil" + "github.com/bitrise-io/go-xcode/exportoptions" + "github.com/bitrise-io/go-xcode/plistutil" + "github.com/fullsailor/pkcs7" + "howett.net/plist" +) + +// ProvisioningProfileInfoModel ... +type ProvisioningProfileInfoModel struct { + UUID string + Name string + TeamName string + TeamID string + BundleID string + ExportType exportoptions.Method + ProvisionedDevices []string + DeveloperCertificates []certificateutil.CertificateInfoModel + CreationDate time.Time + ExpirationDate time.Time + Entitlements plistutil.PlistData + ProvisionsAllDevices bool + Type ProfileType +} + +// PrintableProvisioningProfileInfo ... +func (info ProvisioningProfileInfoModel) String(installedCertificates ...certificateutil.CertificateInfoModel) string { + printable := map[string]interface{}{} + printable["name"] = fmt.Sprintf("%s (%s)", info.Name, info.UUID) + printable["export_type"] = string(info.ExportType) + printable["team"] = fmt.Sprintf("%s (%s)", info.TeamName, info.TeamID) + printable["bundle_id"] = info.BundleID + printable["expire"] = info.ExpirationDate.String() + printable["is_xcode_managed"] = info.IsXcodeManaged() + if info.ProvisionedDevices != nil { + printable["devices"] = info.ProvisionedDevices + } + + certificates := []map[string]interface{}{} + for _, certificateInfo := range info.DeveloperCertificates { + certificate := map[string]interface{}{} + certificate["name"] = certificateInfo.CommonName + certificate["serial"] = certificateInfo.Serial + certificate["team_id"] = certificateInfo.TeamID + certificates = append(certificates, certificate) + } + printable["certificates"] = certificates + + errors := []string{} + if installedCertificates != nil && !info.HasInstalledCertificate(installedCertificates) { + errors = append(errors, "none of the profile's certificates are installed") + } + if err := info.CheckValidity(); err != nil { + errors = append(errors, err.Error()) + } + if len(errors) > 0 { + printable["errors"] = errors + } + + data, err := json.MarshalIndent(printable, "", "\t") + if err != nil { + log.Errorf("Failed to marshal: %v, error: %s", printable, err) + return "" + } + + return string(data) +} + +// IsXcodeManaged ... +func IsXcodeManaged(profileName string) bool { + if strings.HasPrefix(profileName, "XC") { + return true + } + if strings.HasPrefix(profileName, "iOS Team") && strings.Contains(profileName, "Provisioning Profile") { + return true + } + if strings.HasPrefix(profileName, "tvOS Team") && strings.Contains(profileName, "Provisioning Profile") { + return true + } + if strings.HasPrefix(profileName, "Mac Team") && strings.Contains(profileName, "Provisioning Profile") { + return true + } + return false +} + +// IsXcodeManaged ... +func (info ProvisioningProfileInfoModel) IsXcodeManaged() bool { + return IsXcodeManaged(info.Name) +} + +// CheckValidity ... +func (info ProvisioningProfileInfoModel) CheckValidity() error { + timeNow := time.Now() + if !timeNow.Before(info.ExpirationDate) { + return fmt.Errorf("Provisioning Profile is not valid anymore - validity ended at: %s", info.ExpirationDate) + } + return nil +} + +// HasInstalledCertificate ... +func (info ProvisioningProfileInfoModel) HasInstalledCertificate(installedCertificates []certificateutil.CertificateInfoModel) bool { + has := false + for _, certificate := range info.DeveloperCertificates { + for _, installedCertificate := range installedCertificates { + if certificate.Serial == installedCertificate.Serial { + has = true + break + } + } + } + return has +} + +// NewProvisioningProfileInfo ... +func NewProvisioningProfileInfo(provisioningProfile pkcs7.PKCS7) (ProvisioningProfileInfoModel, error) { + var data plistutil.PlistData + if _, err := plist.Unmarshal(provisioningProfile.Content, &data); err != nil { + return ProvisioningProfileInfoModel{}, err + } + + platform, _ := data.GetStringArray("Platform") + profileType := ProfileTypeMacOs + if len(platform) != 0 { + if strings.ToLower(platform[0]) == string(ProfileTypeIos) { + profileType = ProfileTypeIos + } + } + + profile := PlistData(data) + info := ProvisioningProfileInfoModel{ + UUID: profile.GetUUID(), + Name: profile.GetName(), + TeamName: profile.GetTeamName(), + TeamID: profile.GetTeamID(), + BundleID: profile.GetBundleIdentifier(), + CreationDate: profile.GetCreationDate(), + ExpirationDate: profile.GetExpirationDate(), + ProvisionsAllDevices: profile.GetProvisionsAllDevices(), + Type: profileType, + } + + info.ExportType = profile.GetExportMethod() + + if devicesList := profile.GetProvisionedDevices(); devicesList != nil { + info.ProvisionedDevices = devicesList + } + + developerCertificates, found := data.GetByteArrayArray("DeveloperCertificates") + if found { + certificates := []*x509.Certificate{} + for _, certificateBytes := range developerCertificates { + certificate, err := certificateutil.CertificateFromDERContent(certificateBytes) + if err == nil && certificate != nil { + certificates = append(certificates, certificate) + } + } + + for _, certificate := range certificates { + if certificate != nil { + info.DeveloperCertificates = append(info.DeveloperCertificates, certificateutil.NewCertificateInfo(*certificate, nil)) + } + } + } + + info.Entitlements = profile.GetEntitlements() + + return info, nil +} + +// NewProvisioningProfileInfoFromFile ... +func NewProvisioningProfileInfoFromFile(pth string) (ProvisioningProfileInfoModel, error) { + provisioningProfile, err := ProvisioningProfileFromFile(pth) + if err != nil { + return ProvisioningProfileInfoModel{}, err + } + if provisioningProfile != nil { + return NewProvisioningProfileInfo(*provisioningProfile) + } + return ProvisioningProfileInfoModel{}, errors.New("failed to parse provisioning profile infos") +} + +// InstalledProvisioningProfileInfos ... +func InstalledProvisioningProfileInfos(profileType ProfileType) ([]ProvisioningProfileInfoModel, error) { + provisioningProfiles, err := InstalledProvisioningProfiles(profileType) + if err != nil { + return nil, err + } + + infos := []ProvisioningProfileInfoModel{} + for _, provisioningProfile := range provisioningProfiles { + if provisioningProfile != nil { + info, err := NewProvisioningProfileInfo(*provisioningProfile) + if err != nil { + return nil, err + } + infos = append(infos, info) + } + } + return infos, nil +} + +// FindProvisioningProfileInfo ... +func FindProvisioningProfileInfo(uuid string) (ProvisioningProfileInfoModel, string, error) { + profile, pth, err := FindProvisioningProfile(uuid) + if err != nil { + return ProvisioningProfileInfoModel{}, "", err + } + if pth == "" || profile == nil { + return ProvisioningProfileInfoModel{}, "", nil + } + + info, err := NewProvisioningProfileInfo(*profile) + if err != nil { + return ProvisioningProfileInfoModel{}, "", err + } + return info, pth, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/profileutil/plist_data.go b/vendor/github.com/bitrise-io/go-xcode/profileutil/plist_data.go new file mode 100644 index 0000000..4a4c5d1 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/profileutil/plist_data.go @@ -0,0 +1,175 @@ +package profileutil + +import ( + "strings" + "time" + + "github.com/bitrise-io/go-xcode/exportoptions" + "github.com/bitrise-io/go-xcode/plistutil" + "howett.net/plist" +) + +const ( + notValidParameterErrorMessage = "security: SecPolicySetValue: One or more parameters passed to a function were not valid." +) + +// PlistData ... +type PlistData plistutil.PlistData + +// NewPlistDataFromFile ... +func NewPlistDataFromFile(provisioningProfilePth string) (PlistData, error) { + provisioningProfilePKCS7, err := ProvisioningProfileFromFile(provisioningProfilePth) + if err != nil { + return PlistData{}, err + } + + var plistData plistutil.PlistData + if _, err := plist.Unmarshal(provisioningProfilePKCS7.Content, &plistData); err != nil { + return PlistData{}, err + } + + return PlistData(plistData), nil +} + +// GetUUID ... +func (profile PlistData) GetUUID() string { + data := plistutil.PlistData(profile) + uuid, _ := data.GetString("UUID") + return uuid +} + +// GetName ... +func (profile PlistData) GetName() string { + data := plistutil.PlistData(profile) + uuid, _ := data.GetString("Name") + return uuid +} + +// GetApplicationIdentifier ... +func (profile PlistData) GetApplicationIdentifier() string { + data := plistutil.PlistData(profile) + entitlements, ok := data.GetMapStringInterface("Entitlements") + if !ok { + return "" + } + + applicationID, ok := entitlements.GetString("application-identifier") + if !ok { + applicationID, ok = entitlements.GetString("com.apple.application-identifier") + if !ok { + return "" + } + } + return applicationID +} + +// GetBundleIdentifier ... +func (profile PlistData) GetBundleIdentifier() string { + applicationID := profile.GetApplicationIdentifier() + + plistData := plistutil.PlistData(profile) + prefixes, found := plistData.GetStringArray("ApplicationIdentifierPrefix") + if found { + for _, prefix := range prefixes { + applicationID = strings.TrimPrefix(applicationID, prefix+".") + } + } + + teamID := profile.GetTeamID() + return strings.TrimPrefix(applicationID, teamID+".") +} + +// GetExportMethod ... +func (profile PlistData) GetExportMethod() exportoptions.Method { + data := plistutil.PlistData(profile) + entitlements, _ := data.GetMapStringInterface("Entitlements") + platform, _ := data.GetStringArray("Platform") + + if len(platform) != 0 { + switch strings.ToLower(platform[0]) { + case "osx": + _, ok := data.GetStringArray("ProvisionedDevices") + if !ok { + if allDevices, ok := data.GetBool("ProvisionsAllDevices"); ok && allDevices { + return exportoptions.MethodDeveloperID + } + return exportoptions.MethodAppStore + } + return exportoptions.MethodDevelopment + case "ios", "tvos": + _, ok := data.GetStringArray("ProvisionedDevices") + if !ok { + if allDevices, ok := data.GetBool("ProvisionsAllDevices"); ok && allDevices { + return exportoptions.MethodEnterprise + } + return exportoptions.MethodAppStore + } + if allow, ok := entitlements.GetBool("get-task-allow"); ok && allow { + return exportoptions.MethodDevelopment + } + return exportoptions.MethodAdHoc + } + } + + return exportoptions.MethodDefault +} + +// GetEntitlements ... +func (profile PlistData) GetEntitlements() plistutil.PlistData { + data := plistutil.PlistData(profile) + entitlements, _ := data.GetMapStringInterface("Entitlements") + return entitlements +} + +// GetTeamID ... +func (profile PlistData) GetTeamID() string { + data := plistutil.PlistData(profile) + entitlements, ok := data.GetMapStringInterface("Entitlements") + if ok { + teamID, _ := entitlements.GetString("com.apple.developer.team-identifier") + return teamID + } + return "" +} + +// GetExpirationDate ... +func (profile PlistData) GetExpirationDate() time.Time { + data := plistutil.PlistData(profile) + expiry, _ := data.GetTime("ExpirationDate") + return expiry +} + +// GetProvisionedDevices ... +func (profile PlistData) GetProvisionedDevices() []string { + data := plistutil.PlistData(profile) + devices, _ := data.GetStringArray("ProvisionedDevices") + return devices +} + +// GetDeveloperCertificates ... +func (profile PlistData) GetDeveloperCertificates() [][]byte { + data := plistutil.PlistData(profile) + developerCertificates, _ := data.GetByteArrayArray("DeveloperCertificates") + return developerCertificates +} + +// GetTeamName ... +func (profile PlistData) GetTeamName() string { + data := plistutil.PlistData(profile) + teamName, _ := data.GetString("TeamName") + return teamName +} + +// GetCreationDate ... +func (profile PlistData) GetCreationDate() time.Time { + data := plistutil.PlistData(profile) + creationDate, _ := data.GetTime("CreationDate") + return creationDate +} + +// GetProvisionsAllDevices ... +func (profile PlistData) GetProvisionsAllDevices() bool { + data := plistutil.PlistData(profile) + provisionsAlldevices, _ := data.GetBool("ProvisionsAllDevices") + return provisionsAlldevices +} diff --git a/vendor/github.com/bitrise-io/go-xcode/profileutil/util.go b/vendor/github.com/bitrise-io/go-xcode/profileutil/util.go new file mode 100644 index 0000000..e39de14 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/profileutil/util.go @@ -0,0 +1,108 @@ +package profileutil + +import ( + "path/filepath" + + "github.com/bitrise-io/go-utils/fileutil" + "github.com/bitrise-io/go-utils/pathutil" + "github.com/bitrise-io/go-xcode/utility" + "github.com/fullsailor/pkcs7" +) + +// ProfileType ... +type ProfileType string + +// ProfileTypeIos ... +const ProfileTypeIos ProfileType = "ios" + +// ProfileTypeMacOs ... +const ProfileTypeMacOs ProfileType = "macOs" + +// ProvProfileSystemDirPath ... +const ProvProfileSystemDirPath = "~/Library/MobileDevice/Provisioning Profiles" + +// ProvisioningProfileFromContent ... +func ProvisioningProfileFromContent(content []byte) (*pkcs7.PKCS7, error) { + return pkcs7.Parse(content) +} + +// ProvisioningProfileFromFile ... +func ProvisioningProfileFromFile(pth string) (*pkcs7.PKCS7, error) { + content, err := fileutil.ReadBytesFromFile(pth) + if err != nil { + return nil, err + } + return ProvisioningProfileFromContent(content) +} + +// InstalledProvisioningProfiles ... +func InstalledProvisioningProfiles(profileType ProfileType) ([]*pkcs7.PKCS7, error) { + ext := ".mobileprovision" + if profileType == ProfileTypeMacOs { + ext = ".provisionprofile" + } + + absProvProfileDirPath, err := pathutil.AbsPath(ProvProfileSystemDirPath) + if err != nil { + return nil, err + } + + pattern := filepath.Join(utility.EscapeGlobPath(absProvProfileDirPath), "*"+ext) + pths, err := filepath.Glob(pattern) + if err != nil { + return nil, err + } + + profiles := []*pkcs7.PKCS7{} + for _, pth := range pths { + profile, err := ProvisioningProfileFromFile(pth) + if err != nil { + return nil, err + } + profiles = append(profiles, profile) + } + return profiles, nil +} + +// FindProvisioningProfile ... +func FindProvisioningProfile(uuid string) (*pkcs7.PKCS7, string, error) { + { + iosProvisioningProfileExt := ".mobileprovision" + absProvProfileDirPath, err := pathutil.AbsPath(ProvProfileSystemDirPath) + if err != nil { + return nil, "", err + } + + pth := filepath.Join(absProvProfileDirPath, uuid+iosProvisioningProfileExt) + if exist, err := pathutil.IsPathExists(pth); err != nil { + return nil, "", err + } else if exist { + profile, err := ProvisioningProfileFromFile(pth) + if err != nil { + return nil, "", err + } + return profile, pth, nil + } + } + + { + macOsProvisioningProfileExt := ".provisionprofile" + absProvProfileDirPath, err := pathutil.AbsPath(ProvProfileSystemDirPath) + if err != nil { + return nil, "", err + } + + pth := filepath.Join(absProvProfileDirPath, uuid+macOsProvisioningProfileExt) + if exist, err := pathutil.IsPathExists(pth); err != nil { + return nil, "", err + } else if exist { + profile, err := ProvisioningProfileFromFile(pth) + if err != nil { + return nil, "", err + } + return profile, pth, nil + } + } + + return nil, "", nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/utility/glob.go b/vendor/github.com/bitrise-io/go-xcode/utility/glob.go new file mode 100644 index 0000000..1333c6f --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/utility/glob.go @@ -0,0 +1,13 @@ +package utility + +// EscapeGlobPath escapes a partial path, determined at runtime, used as a parameter for filepath.Glob +func EscapeGlobPath(path string) string { + var escaped string + for _, ch := range path { + if ch == '[' || ch == ']' || ch == '-' || ch == '*' || ch == '?' || ch == '\\' { + escaped += "\\" + } + escaped += string(ch) + } + return escaped +} diff --git a/vendor/github.com/bitrise-io/go-xcode/utility/path.go b/vendor/github.com/bitrise-io/go-xcode/utility/path.go new file mode 100644 index 0000000..45830d3 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/utility/path.go @@ -0,0 +1,103 @@ +package utility + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "strings" + + "github.com/bitrise-io/go-utils/pathutil" +) + +// FilterFunc ... +type FilterFunc func(pth string) (bool, error) + +// FilterPaths ... +func FilterPaths(paths []string, filters ...FilterFunc) ([]string, error) { + filtered := []string{} + + for _, pth := range paths { + allowed := true + for _, filter := range filters { + if allows, err := filter(pth); err != nil { + return []string{}, err + } else if !allows { + allowed = false + break + } + } + if allowed { + filtered = append(filtered, pth) + } + } + + return filtered, nil +} + +// ListEntries ... +func ListEntries(dir string, filters ...FilterFunc) ([]string, error) { + absDir, err := filepath.Abs(dir) + if err != nil { + return []string{}, err + } + + entries, err := ioutil.ReadDir(absDir) + if err != nil { + return []string{}, err + } + + paths := []string{} + for _, entry := range entries { + pth := filepath.Join(absDir, entry.Name()) + paths = append(paths, pth) + } + + return FilterPaths(paths, filters...) +} + +// ExtensionFilter ... +func ExtensionFilter(ext string, allowed bool) FilterFunc { + return func(pth string) (bool, error) { + e := filepath.Ext(pth) + return (allowed == strings.EqualFold(ext, e)), nil + } +} + +// BaseFilter ... +func BaseFilter(base string, allowed bool) FilterFunc { + return func(pth string) (bool, error) { + b := filepath.Base(pth) + return (allowed == strings.EqualFold(base, b)), nil + } +} + +// FindFileInAppDir ... +func FindFileInAppDir(appDir, fileName string) (string, error) { + filePth := filepath.Join(appDir, fileName) + if exist, err := pathutil.IsPathExists(filePth); err != nil { + return "", err + } else if exist { + return filePth, nil + } + // --- + + // It's somewhere else - let's find it! + apps, err := ListEntries(appDir, ExtensionFilter(".app", true)) + if err != nil { + return "", err + } + + for _, app := range apps { + pths, err := ListEntries(app, BaseFilter(fileName, true)) + if err != nil { + return "", err + } + + if len(pths) > 0 { + return pths[0], nil + } + } + // --- + + return "", fmt.Errorf("failed to find %s", fileName) +} diff --git a/vendor/github.com/bitrise-io/go-xcode/utility/utility.go b/vendor/github.com/bitrise-io/go-xcode/utility/utility.go new file mode 100644 index 0000000..5d605c5 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/utility/utility.go @@ -0,0 +1,51 @@ +package utility + +import ( + "fmt" + "strconv" + "strings" + + "github.com/bitrise-io/go-utils/command" + "github.com/bitrise-io/go-xcode/models" +) + +func getXcodeVersionFromXcodebuildOutput(outStr string) (models.XcodebuildVersionModel, error) { + split := strings.Split(outStr, "\n") + if len(split) == 0 { + return models.XcodebuildVersionModel{}, fmt.Errorf("failed to parse xcodebuild version output (%s)", outStr) + } + + xcodebuildVersion := split[0] + buildVersion := split[1] + + split = strings.Split(xcodebuildVersion, " ") + if len(split) != 2 { + return models.XcodebuildVersionModel{}, fmt.Errorf("failed to parse xcodebuild version output (%s)", outStr) + } + + version := split[1] + + split = strings.Split(version, ".") + majorVersionStr := split[0] + + majorVersion, err := strconv.ParseInt(majorVersionStr, 10, 32) + if err != nil { + return models.XcodebuildVersionModel{}, fmt.Errorf("failed to parse xcodebuild version output (%s), error: %s", outStr, err) + } + + return models.XcodebuildVersionModel{ + Version: xcodebuildVersion, + BuildVersion: buildVersion, + MajorVersion: majorVersion, + }, nil +} + +// GetXcodeVersion ... +func GetXcodeVersion() (models.XcodebuildVersionModel, error) { + cmd := command.New("xcodebuild", "-version") + outStr, err := cmd.RunAndReturnTrimmedCombinedOutput() + if err != nil { + return models.XcodebuildVersionModel{}, fmt.Errorf("xcodebuild -version failed, err: %s, details: %s", err, outStr) + } + return getXcodeVersionFromXcodebuildOutput(outStr) +} diff --git a/vendor/github.com/bitrise-io/xcode-project/.gitignore b/vendor/github.com/bitrise-io/xcode-project/.gitignore new file mode 100644 index 0000000..29260c6 --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/.gitignore @@ -0,0 +1,5 @@ +.bitrise* +Gopkg.lock +Gopkg.toml +vendor/ +.idea/ \ No newline at end of file diff --git a/vendor/github.com/bitrise-io/xcode-project/README.md b/vendor/github.com/bitrise-io/xcode-project/README.md new file mode 100644 index 0000000..86fa465 --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/README.md @@ -0,0 +1 @@ +# xcode-project-parser diff --git a/vendor/github.com/bitrise-io/xcode-project/bitrise.yml b/vendor/github.com/bitrise-io/xcode-project/bitrise.yml new file mode 100644 index 0000000..704a44a --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/bitrise.yml @@ -0,0 +1,31 @@ +format_version: "5" +default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git + +project_type: other + +workflows: + test: + steps: + - script: + title: Install testing packages + run_if: .IsCI + inputs: + - content: |- + #!/bin/bash + set -ex + go get -u -v howett.net/plist + go get -u -v github.com/stretchr/testify/require + go get -u -v github.com/bitrise-io/go-utils/command + go get -u -v github.com/bitrise-io/go-utils/fileutil + go get -u -v github.com/bitrise-io/go-utils/pathutil + go get -u -v golang.org/x/text/unicode/norm + go get -u github.com/google/go-cmp/cmp + - go-list: + - golint: + - errcheck: + - go-test: + - codecov: + run_if: .IsCI + inputs: + - other_options: -f ${GO_CODE_COVERAGE_REPORT_PATH} + - CODECOV_TOKEN: "$CODECOV_UPLOAD_TOKEN" diff --git a/vendor/github.com/bitrise-io/xcode-project/pretty/pretty.go b/vendor/github.com/bitrise-io/xcode-project/pretty/pretty.go new file mode 100644 index 0000000..c2fa795 --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/pretty/pretty.go @@ -0,0 +1,16 @@ +package pretty + +import ( + "encoding/json" + "fmt" +) + +// Object returns the json representation of the given parameter +// if json marshal fails it returns the parameter's default format +func Object(o interface{}) string { + b, err := json.MarshalIndent(o, "", "\t") + if err != nil { + return fmt.Sprint(o) + } + return string(b) +} diff --git a/vendor/github.com/bitrise-io/xcode-project/project.go b/vendor/github.com/bitrise-io/xcode-project/project.go new file mode 100644 index 0000000..77c996a --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/project.go @@ -0,0 +1,27 @@ +package project + +import ( + "github.com/bitrise-io/xcode-project/xcodeproj" + "github.com/bitrise-io/xcode-project/xcscheme" + "github.com/bitrise-io/xcode-project/xcworkspace" +) + +// HasScheme represents a struct that implements Scheme. +type HasScheme interface { + Scheme(string) (*xcscheme.Scheme, string, error) +} + +// Scheme returns the project or workspace scheme by name. +func Scheme(pth string, name string) (*xcscheme.Scheme, string, error) { + var p HasScheme + var err error + if xcodeproj.IsXcodeProj(pth) { + p, err = xcodeproj.Open(pth) + } else { + p, err = xcworkspace.Open(pth) + } + if err != nil { + return nil, "", err + } + return p.Scheme(name) +} diff --git a/vendor/github.com/bitrise-io/xcode-project/serialized/error.go b/vendor/github.com/bitrise-io/xcode-project/serialized/error.go new file mode 100644 index 0000000..ea63009 --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/serialized/error.go @@ -0,0 +1,54 @@ +package serialized + +import "fmt" + +// KeyNotFoundError ... +type KeyNotFoundError struct { + key string + object Object +} + +// Error ... +func (e KeyNotFoundError) Error() string { + return fmt.Sprintf("key: %T(%#v) not found in: %T(%#v)", e.key, e.key, e.object, e.object) +} + +// NewKeyNotFoundError ... +func NewKeyNotFoundError(key string, object Object) KeyNotFoundError { + return KeyNotFoundError{key: key, object: object} +} + +// IsKeyNotFoundError ... +func IsKeyNotFoundError(err error) bool { + if err == nil { + return false + } + _, ok := err.(KeyNotFoundError) + return ok +} + +// TypeCastError ... +type TypeCastError struct { + key string + value interface{} + expectedType string +} + +// NewTypeCastError ... +func NewTypeCastError(key string, value interface{}, expected interface{}) TypeCastError { + return TypeCastError{key: key, value: value, expectedType: fmt.Sprintf("%T", expected)} +} + +// IsTypeCastError ... +func IsTypeCastError(err error) bool { + if err == nil { + return false + } + _, ok := err.(TypeCastError) + return ok +} + +// Error ... +func (e TypeCastError) Error() string { + return fmt.Sprintf("value: %T(%#v) for key: %T(%#v) can not be casted to: %s", e.value, e.value, e.key, e.key, e.expectedType) +} diff --git a/vendor/github.com/bitrise-io/xcode-project/serialized/serialized.go b/vendor/github.com/bitrise-io/xcode-project/serialized/serialized.go new file mode 100644 index 0000000..96c8a29 --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/serialized/serialized.go @@ -0,0 +1,77 @@ +package serialized + +// Object ... +type Object map[string]interface{} + +// Keys ... +func (o Object) Keys() []string { + var keys []string + for key := range o { + keys = append(keys, key) + } + return keys +} + +// Value ... +func (o Object) Value(key string) (interface{}, error) { + value, ok := o[key] + if !ok { + return nil, NewKeyNotFoundError(key, o) + } + return value, nil +} + +// String ... +func (o Object) String(key string) (string, error) { + value, err := o.Value(key) + if err != nil { + return "", err + } + + casted, ok := value.(string) + if !ok { + return "", NewTypeCastError(key, value, "") + } + + return casted, nil +} + +// StringSlice ... +func (o Object) StringSlice(key string) ([]string, error) { + value, err := o.Value(key) + if err != nil { + return nil, err + } + + casted, ok := value.([]interface{}) + if !ok { + return nil, NewTypeCastError(key, value, []interface{}{}) + } + + slice := []string{} + for _, v := range casted { + item, ok := v.(string) + if !ok { + return nil, NewTypeCastError(key, casted, "") + } + + slice = append(slice, item) + } + + return slice, nil +} + +// Object ... +func (o Object) Object(key string) (Object, error) { + value, err := o.Value(key) + if err != nil { + return nil, err + } + + casted, ok := value.(map[string]interface{}) + if !ok { + return nil, NewTypeCastError(key, value, map[string]interface{}{}) + } + + return casted, nil +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcodebuild/xcodebuild.go b/vendor/github.com/bitrise-io/xcode-project/xcodebuild/xcodebuild.go new file mode 100644 index 0000000..261d165 --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcodebuild/xcodebuild.go @@ -0,0 +1,74 @@ +package xcodebuild + +import ( + "fmt" + "strings" + + "github.com/bitrise-io/go-utils/command" + "github.com/bitrise-io/go-utils/errorutil" + "github.com/bitrise-io/xcode-project/serialized" +) + +func parseShowBuildSettingsOutput(out string) serialized.Object { + settings := serialized.Object{} + + lines := strings.Split(out, "\n") + for _, line := range lines { + split := strings.Split(line, " = ") + + if len(split) < 2 { + continue + } + + key := strings.TrimSpace(split[0]) + if key == "" { + continue + } + + value := strings.TrimSpace(strings.Join(split[1:], " = ")) + + settings[key] = value + } + + return settings +} + +// ShowProjectBuildSettings ... +func ShowProjectBuildSettings(project, target, configuration string, customOptions ...string) (serialized.Object, error) { + args := []string{"-project", project, "-target", target, "-configuration", configuration} + args = append(args, "-showBuildSettings") + args = append(args, customOptions...) + + cmd := command.New("xcodebuild", args...) + + out, err := cmd.RunAndReturnTrimmedCombinedOutput() + if err != nil { + if errorutil.IsExitStatusError(err) { + return nil, fmt.Errorf("%s command failed: output: %s", cmd.PrintableCommandArgs(), out) + } + + return nil, fmt.Errorf("failed to run command %s: %s", cmd.PrintableCommandArgs(), err) + } + + return parseShowBuildSettingsOutput(out), nil +} + +// ShowWorkspaceBuildSettings ... +func ShowWorkspaceBuildSettings(workspace, scheme, configuration string, customOptions ...string) (serialized.Object, error) { + args := []string{"-workspace", workspace, "-scheme", scheme, "-configuration", configuration} + args = append(args, "-showBuildSettings") + args = append(args, customOptions...) + + cmd := command.New("xcodebuild", args...) + + out, err := cmd.RunAndReturnTrimmedCombinedOutput() + if err != nil { + if errorutil.IsExitStatusError(err) { + return nil, fmt.Errorf("%s command failed: output: %s", cmd.PrintableCommandArgs(), out) + } + + return nil, fmt.Errorf("failed to run command %s: %s", cmd.PrintableCommandArgs(), err) + } + + return parseShowBuildSettingsOutput(out), nil +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcodeproj/appiconset.go b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/appiconset.go new file mode 100644 index 0000000..d5cd482 --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/appiconset.go @@ -0,0 +1,170 @@ +package xcodeproj + +import ( + "fmt" + "path" + "path/filepath" + "regexp" + "strings" + + "github.com/bitrise-io/go-utils/pathutil" + "github.com/bitrise-io/go-utils/sliceutil" + "github.com/bitrise-io/xcode-project/serialized" +) + +// TargetsToAppIconSets maps target names to an array app icon set absolute paths. +type TargetsToAppIconSets map[string][]string + +// AppIconSetPaths parses an Xcode project and returns targets mapped to app icon set absolute paths. +func AppIconSetPaths(projectPath string) (TargetsToAppIconSets, error) { + absPth, err := pathutil.AbsPath(projectPath) + if err != nil { + return TargetsToAppIconSets{}, err + } + + _, _, objects, projectID, err := open(absPth) + proj, err := parseProj(projectID, objects) + if err != nil { + return TargetsToAppIconSets{}, err + } + + return appIconSetPaths(proj, projectPath, objects) +} + +func appIconSetPaths(project Proj, projectPath string, objects serialized.Object) (TargetsToAppIconSets, error) { + type iconTarget struct { + target Target + appIconSetNames []string + } + + targetToAppIcons := map[string][]string{} + for _, target := range project.Targets { + appIconSetNames := getAppIconSetNames(target) + if len(appIconSetNames) == 0 { + continue + } + + assetCatalogs, err := assetCatalogs(target, project.ID, objects) + if err != nil { + return nil, err + } else if len(assetCatalogs) == 0 { + continue + } + + appIcons := []string{} + for _, appIconSetName := range appIconSetNames { + appIconSetPaths, err := lookupAppIconPaths(projectPath, assetCatalogs, appIconSetName, project.ID, objects) + if err != nil { + return nil, err + } else if len(appIconSetPaths) == 0 { + return nil, fmt.Errorf("not found app icon set (%s) on paths: %s", appIconSetName, assetCatalogs) + } + appIcons = append(appIcons, appIconSetPaths...) + } + targetToAppIcons[target.ID] = sliceutil.UniqueStringSlice(appIcons) + } + + return targetToAppIcons, nil +} + +func lookupAppIconPaths(projectPath string, assetCatalogs []fileReference, appIconSetName string, projectID string, objects serialized.Object) ([]string, error) { + for _, fileReference := range assetCatalogs { + resolvedPath, err := resolveObjectAbsolutePath(fileReference.id, projectID, projectPath, objects) + if err != nil { + return nil, err + } else if resolvedPath == "" { + return nil, fmt.Errorf("could not resolve path") + } + + re := regexp.MustCompile(`\$\{(.+)\}`) + wildcharAppIconSetName := re.ReplaceAllString(appIconSetName, "*") + + matches, err := filepath.Glob(path.Join(regexp.QuoteMeta(resolvedPath), wildcharAppIconSetName+".appiconset")) + if err != nil { + return nil, err + } + return matches, nil + } + return nil, nil +} + +func assetCatalogs(target Target, projectID string, objects serialized.Object) ([]fileReference, error) { + if target.Type == NativeTargetType { // Ignoring PBXAggregateTarget and PBXLegacyTarget as may not contain buildPhases key + resourcesBuildPhase, err := filterResourcesBuildPhase(target.buildPhaseIDs, objects) + if err != nil { + return nil, fmt.Errorf("getting resource build phases failed, error: %s", err) + } + assetCatalogs, err := filterAssetCatalogs(resourcesBuildPhase, projectID, objects) + if err != nil { + return nil, err + } + return assetCatalogs, nil + } + return nil, nil +} + +func filterResourcesBuildPhase(buildPhases []string, objects serialized.Object) (resourcesBuildPhase, error) { + for _, buildPhaseUUID := range buildPhases { + rawBuildPhase, err := objects.Object(buildPhaseUUID) + if err != nil { + return resourcesBuildPhase{}, err + } + if isResourceBuildPhase(rawBuildPhase) { + buildPhase, err := parseResourcesBuildPhase(buildPhaseUUID, objects) + if err != nil { + return resourcesBuildPhase{}, fmt.Errorf("failed to parse ResourcesBuildPhase, error: %s", err) + } + return buildPhase, nil + } + } + return resourcesBuildPhase{}, fmt.Errorf("resource build phase not found") +} + +func filterAssetCatalogs(buildPhase resourcesBuildPhase, projectID string, objects serialized.Object) ([]fileReference, error) { + assetCatalogs := []fileReference{} + for _, fileUUID := range buildPhase.files { + buildFile, err := parseBuildFile(fileUUID, objects) + if err != nil { + // ignore: + // D0177B971F26869C0044446D /* (null) in Resources */ = {isa = PBXBuildFile; }; + continue + } + + // can be PBXVariantGroup or PBXFileReference + rawElement, err := objects.Object(buildFile.fileRef) + if err != nil { + return nil, err + } + if ok, err := isFileReference(rawElement); err != nil { + return nil, err + } else if !ok { + // ignore PBXVariantGroup + continue + } + + fileReference, err := parseFileReference(buildFile.fileRef, objects) + if err != nil { + return nil, err + } + + if strings.HasSuffix(fileReference.path, ".xcassets") { + assetCatalogs = append(assetCatalogs, fileReference) + } + } + return assetCatalogs, nil +} + +func getAppIconSetNames(target Target) []string { + const appIconSetNameKey = "ASSETCATALOG_COMPILER_APPICON_NAME" + + appIconSetNames := []string{} + for _, configuration := range target.BuildConfigurationList.BuildConfigurations { + appIconSetName, err := configuration.BuildSettings.String(appIconSetNameKey) + if err != nil { + return nil + } + appIconSetNames = append(appIconSetNames, appIconSetName) + } + + return appIconSetNames +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcodeproj/attributes.go b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/attributes.go new file mode 100644 index 0000000..5973f60 --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/attributes.go @@ -0,0 +1,61 @@ +package xcodeproj + +import ( + "fmt" + + "github.com/bitrise-io/xcode-project/serialized" +) + +// ProjectAtributes ... +// +// **Deprecated**: use the func (p XcodeProj) Attributes() (serialized.Object, error) method instead +type ProjectAtributes struct { + TargetAttributes serialized.Object +} + +// +// **Deprecated**: use the func (p XcodeProj) Attributes() (serialized.Object, error) method instead +func parseProjectAttributes(rawPBXProj serialized.Object) (ProjectAtributes, error) { + var attributes ProjectAtributes + attributesObject, err := rawPBXProj.Object("attributes") + if err != nil { + return ProjectAtributes{}, err + } + + attributes.TargetAttributes, err = parseTargetAttributes(attributesObject) + if err != nil && !serialized.IsKeyNotFoundError(err) { + return ProjectAtributes{}, err + } + + return attributes, nil +} + +// +// **Deprecated**: use the func (p XcodeProj) TargetAttributes() (serialized.Object, error) method instead +func parseTargetAttributes(attributesObject serialized.Object) (serialized.Object, error) { + return attributesObject.Object("TargetAttributes") +} + +// Attributes ... +func (p XcodeProj) Attributes() (serialized.Object, error) { + objects, err := p.RawProj.Object("objects") + if err != nil { + return nil, fmt.Errorf("failed to fetch project attributes, the objects of the project are not found, error: %s", err) + } + + object, err := objects.Object(p.Proj.ID) + if err != nil { + return nil, fmt.Errorf("failed to fetch project attributes, the project objects wit ID (%s) is not found, error: %s", p.Proj.ID, err) + } + + return object.Object("attributes") +} + +// TargetAttributes ... +func (p XcodeProj) TargetAttributes() (serialized.Object, error) { + attributes, err := p.Attributes() + if err != nil { + return nil, err + } + return attributes.Object("TargetAttributes") +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcodeproj/build_configuration.go b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/build_configuration.go new file mode 100644 index 0000000..1e72e5c --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/build_configuration.go @@ -0,0 +1,33 @@ +package xcodeproj + +import "github.com/bitrise-io/xcode-project/serialized" + +// BuildConfiguration .. +type BuildConfiguration struct { + ID string + Name string + BuildSettings serialized.Object +} + +func parseBuildConfiguration(id string, objects serialized.Object) (BuildConfiguration, error) { + raw, err := objects.Object(id) + if err != nil { + return BuildConfiguration{}, err + } + + name, err := raw.String("name") + if err != nil { + return BuildConfiguration{}, err + } + + buildSettings, err := raw.Object("buildSettings") + if err != nil { + return BuildConfiguration{}, err + } + + return BuildConfiguration{ + ID: id, + Name: name, + BuildSettings: buildSettings, + }, nil +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcodeproj/configuration_list.go b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/configuration_list.go new file mode 100644 index 0000000..f63a6e2 --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/configuration_list.go @@ -0,0 +1,89 @@ +package xcodeproj + +import ( + "fmt" + + "github.com/bitrise-io/xcode-project/serialized" +) + +// ConfigurationList ... +type ConfigurationList struct { + ID string + DefaultConfigurationName string + BuildConfigurations []BuildConfiguration +} + +func parseConfigurationList(id string, objects serialized.Object) (ConfigurationList, error) { + raw, err := objects.Object(id) + if err != nil { + return ConfigurationList{}, err + } + + rawBuildConfigurations, err := raw.StringSlice("buildConfigurations") + if err != nil { + return ConfigurationList{}, err + } + + var buildConfigurations []BuildConfiguration + for _, rawID := range rawBuildConfigurations { + buildConfiguration, err := parseBuildConfiguration(rawID, objects) + if err != nil { + return ConfigurationList{}, err + } + + buildConfigurations = append(buildConfigurations, buildConfiguration) + } + + var defaultConfigurationName string + if aDefaultConfigurationName, err := raw.String("defaultConfigurationName"); err == nil { + defaultConfigurationName = aDefaultConfigurationName + } + + return ConfigurationList{ + ID: id, + DefaultConfigurationName: defaultConfigurationName, + BuildConfigurations: buildConfigurations, + }, nil +} + +// BuildConfigurationList ... +func (p XcodeProj) BuildConfigurationList(targetID string) (serialized.Object, error) { + objects, err := p.RawProj.Object("objects") + if err != nil { + return nil, fmt.Errorf("failed to read project: %s", err) + } + + object, err := objects.Object(targetID) + if err != nil { + return nil, fmt.Errorf("failed to read target (%s) object: %s", targetID, err) + } + buildConfigurationListID, err := object.String("buildConfigurationList") + if err != nil { + return nil, fmt.Errorf("failed to read target (%s) build configuration list: %s", targetID, err) + } + + return objects.Object(buildConfigurationListID) +} + +// BuildConfigurations ... +func (p XcodeProj) BuildConfigurations(buildConfigurationList serialized.Object) ([]serialized.Object, error) { + buildConfigurationIDList, err := buildConfigurationList.StringSlice("buildConfigurations") + if err != nil { + return nil, fmt.Errorf("failed to fetch the buildConfigurations attributes of the the buildConfigurationList (%v), error: %s", buildConfigurationList, err) + } + + var buildConfigurations []serialized.Object + for _, id := range buildConfigurationIDList { + objects, err := p.RawProj.Object("objects") + if err != nil { + return nil, fmt.Errorf("failed to fetch target buildConfigurations, the objects of the project are not found, error: %s", err) + } + + buildConfiguration, err := objects.Object(id) + if err != nil { + return nil, fmt.Errorf("failed to fetch target buildConfiguration objects with ID (%s), error: %s", id, err) + } + buildConfigurations = append(buildConfigurations, buildConfiguration) + } + return buildConfigurations, nil +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcodeproj/plist.go b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/plist.go new file mode 100644 index 0000000..a8d574a --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/plist.go @@ -0,0 +1,53 @@ +package xcodeproj + +import ( + "io/ioutil" + + "github.com/bitrise-io/go-utils/fileutil" + "github.com/bitrise-io/xcode-project/serialized" + "howett.net/plist" +) + +// ReadPlistFile returns a parsed object representing a plist file residing at path +// and a format identifier specifying the plist file format. +// Error is returned if: +// - file at path cannot be read +// - file is not a valid plist file +// Format IDs: +// - XMLFormat = 1 +// - BinaryFormat = 2 +// - OpenStepFormat = 3 +// - GNUStepFormat = 4 +func ReadPlistFile(path string) (serialized.Object, int, error) { + codeSignEntitlementsContent, err := fileutil.ReadBytesFromFile(path) + if err != nil { + return nil, 0, err + } + + var codeSignEntitlements serialized.Object + format, err := plist.Unmarshal(codeSignEntitlementsContent, &codeSignEntitlements) + if err != nil { + return nil, 0, err + } + + return codeSignEntitlements, format, nil +} + +// WritePlistFile writes a parsed object representing a plist file according the +// format identifier specified. +// Error is returned if: +// - file at path cannot be written +// - format id is incorrect +// Valid format IDs: +// - XMLFormat = 1 +// - BinaryFormat = 2 +// - OpenStepFormat = 3 +// - GNUStepFormat = 4 +func WritePlistFile(path string, entitlements serialized.Object, format int) error { + marshalled, err := plist.Marshal(entitlements, format) + if err != nil { + return err + } + + return ioutil.WriteFile(path, marshalled, 0644) +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcodeproj/product_reference.go b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/product_reference.go new file mode 100644 index 0000000..5a6dd0c --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/product_reference.go @@ -0,0 +1,24 @@ +package xcodeproj + +import "github.com/bitrise-io/xcode-project/serialized" + +// ProductReference ... +type ProductReference struct { + Path string +} + +func parseProductReference(id string, objects serialized.Object) (ProductReference, error) { + raw, err := objects.Object(id) + if err != nil { + return ProductReference{}, err + } + + pth, err := raw.String("path") + if err != nil { + return ProductReference{}, err + } + + return ProductReference{ + Path: pth, + }, nil +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcodeproj/proj.go b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/proj.go new file mode 100644 index 0000000..67a45d0 --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/proj.go @@ -0,0 +1,96 @@ +package xcodeproj + +import ( + "github.com/bitrise-io/xcode-project/serialized" +) + +// Proj ... +type Proj struct { + ID string + BuildConfigurationList ConfigurationList + Targets []Target + Attributes ProjectAtributes +} + +func parseProj(id string, objects serialized.Object) (Proj, error) { + rawPBXProj, err := objects.Object(id) + if err != nil { + return Proj{}, err + } + + projectAttributes, err := parseProjectAttributes(rawPBXProj) + if err != nil { + return Proj{}, err + } + + buildConfigurationListID, err := rawPBXProj.String("buildConfigurationList") + if err != nil { + return Proj{}, err + } + + buildConfigurationList, err := parseConfigurationList(buildConfigurationListID, objects) + if err != nil { + return Proj{}, err + } + + rawTargets, err := rawPBXProj.StringSlice("targets") + if err != nil { + return Proj{}, err + } + + var targets []Target + for i := range rawTargets { + // rawTargets can contain more target IDs than the project configuration has + hasTargetNode, err := hasTargetNode(rawTargets[i], objects) + if err != nil { + return Proj{}, err + } + + if !hasTargetNode { + continue + } + + target, err := parseTarget(rawTargets[i], objects) + if err != nil { + return Proj{}, err + } + targets = append(targets, target) + } + + return Proj{ + ID: id, + BuildConfigurationList: buildConfigurationList, + Targets: targets, + Attributes: projectAttributes, + }, nil +} + +func hasTargetNode(id string, objects serialized.Object) (bool, error) { + if _, err := objects.Object(id); err != nil { + if serialized.IsKeyNotFoundError(err) { + return false, nil + } + return false, err + } + return true, nil +} + +// Target ... +func (p Proj) Target(id string) (Target, bool) { + for _, target := range p.Targets { + if target.ID == id { + return target, true + } + } + return Target{}, false +} + +// TargetByName ... +func (p Proj) TargetByName(name string) (Target, bool) { + for _, target := range p.Targets { + if target.Name == name { + return target, true + } + } + return Target{}, false +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcodeproj/resources_build_phase.go b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/resources_build_phase.go new file mode 100644 index 0000000..ff84540 --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/resources_build_phase.go @@ -0,0 +1,250 @@ +package xcodeproj + +import ( + "fmt" + "path" + + "github.com/bitrise-io/go-utils/sliceutil" + + "github.com/bitrise-io/xcode-project/serialized" +) + +// resourcesBuildPhase represents a PBXResourcesBuildPhase element +type resourcesBuildPhase struct { + ID string + files []string +} + +func isResourceBuildPhase(raw serialized.Object) bool { + if isa, err := raw.String("isa"); err != nil { + return false + } else if isa != "PBXResourcesBuildPhase" { + return false + } + return true +} + +func parseResourcesBuildPhase(id string, objects serialized.Object) (resourcesBuildPhase, error) { + rawResourcesBuildPhase, err := objects.Object(id) + if err != nil { + return resourcesBuildPhase{}, err + } + + if !isResourceBuildPhase(rawResourcesBuildPhase) { + return resourcesBuildPhase{}, fmt.Errorf("not a PBXResourcesBuildPhase element") + } + + files, err := rawResourcesBuildPhase.StringSlice("files") + if err != nil { + return resourcesBuildPhase{}, err + } + + return resourcesBuildPhase{ + ID: id, + files: files, + }, nil +} + +// buildFile represents a PBXBuildFile element +// 47C11A4A21FF63970084FD7F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 47C11A4921FF63970084FD7F /* Assets.xcassets */; }; +type buildFile struct { + fileRef string +} + +func parseBuildFile(id string, objects serialized.Object) (buildFile, error) { + rawBuildFile, err := objects.Object(id) + if err != nil { + return buildFile{}, err + } + if isa, err := rawBuildFile.String("isa"); err != nil { + return buildFile{}, err + } else if isa != "PBXBuildFile" { + return buildFile{}, fmt.Errorf("not a PBXBuildFile element") + } + + fileRef, err := rawBuildFile.String("fileRef") + if err != nil { + return buildFile{}, err + } + + return buildFile{ + fileRef: fileRef, + }, nil +} + +type sourceTree int + +const ( + unsupportedParent sourceTree = iota + groupParent + absoluteParentPath + undefinedParent +) + +// PBXFileReference +// 47C11A4921FF63970084FD7F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; +type fileReference struct { + id string + path string +} + +const fileReferenceElementType = "PBXFileReference" + +func isFileReference(raw serialized.Object) (bool, error) { + if isa, err := raw.String("isa"); err != nil { + return false, err + } else if isa == fileReferenceElementType { + return true, nil + } + return false, nil +} + +func parseFileReference(id string, objects serialized.Object) (fileReference, error) { + rawFileReference, err := objects.Object(id) + if err != nil { + return fileReference{}, err + } + + if ok, err := isFileReference(rawFileReference); err != nil { + return fileReference{}, err + } else if !ok { + return fileReference{}, fmt.Errorf("not a %s element", fileReferenceElementType) + } + + path, err := rawFileReference.String("path") + if err != nil { + return fileReference{}, err + } + + return fileReference{ + id: id, + path: path, + }, nil +} + +// PBXGroup example: +// 01801EC11A3360B1002B4718 /* Resources */ = { +// isa = PBXGroup; +// children = ( +// A045E5E11EDC5C1700BC8A92 /* Localizable.strings */, +// 01801EA51A32CA2A002B4718 /* Images.xcassets */, +// ); +// name = Resources; +// sourceTree = ""; +// }; + +func resolveObjectAbsolutePath(targetID string, projectID string, projectPath string, objects serialized.Object) (string, error) { + _, err := objects.Object(targetID) + if err != nil { + return "", err + } + project, err := objects.Object(projectID) + if err != nil { + return "", err + } + + projectDirPath, err := project.String("projectDirPath") + if err != nil { + return "", fmt.Errorf("key projectDirPath not found, project: %s, error: %s", project, err) + } + projectRoot, err := project.String("projectRoot") + if err != nil { + return "", fmt.Errorf("key projectRoot not found, project: %s, error: %s", project, err) + } + mainGroup, err := project.String("mainGroup") + if err != nil { + return "", fmt.Errorf("key mainGroup not found, project: %s, error: %s", project, err) + } + + pathInProjectTree, err := findInProjectTree(targetID, mainGroup, objects, &[]string{}) + if err != nil { + return "", fmt.Errorf("failed to find target ID in project, error: %s", err) + } + pathInProjectTree = append(pathInProjectTree, projectEntry{ + path: path.Join(projectPath, "..", projectDirPath, projectRoot), + pathRelation: absoluteParentPath, + }) + + path, err := resolveFilePath(pathInProjectTree) + if err != nil { + return "", err + } + return path, nil +} + +type projectEntry struct { + id string + pathRelation sourceTree + path string +} + +func findInProjectTree(target string, currentID string, object serialized.Object, visited *[]string) ([]projectEntry, error) { + if sliceutil.IsStringInSlice(currentID, *visited) { + return nil, fmt.Errorf("circular reference in project, id: %s", currentID) + } + *visited = append(*visited, currentID) + + entry, err := object.Object(currentID) + if err != nil { + return nil, fmt.Errorf("object not found, id: %s, error: %s", currentID, err) + } + + entryPath, err := entry.String("path") + if err != nil { + } + sourceTreeRaw, err := entry.String("sourceTree") + if err != nil { + return nil, err + } + var pathRelation sourceTree + switch sourceTreeRaw { + case "": + pathRelation = groupParent + case "": + pathRelation = absoluteParentPath + case "": + pathRelation = undefinedParent + default: + pathRelation = unsupportedParent + } + + treeNode := projectEntry{ + id: currentID, + path: entryPath, + pathRelation: pathRelation, + } + + if currentID == target { + return []projectEntry{treeNode}, nil + } + + childIDs, err := entry.StringSlice("children") + if err != nil { + return nil, nil + } + for _, childID := range childIDs { + pathInProjectTree, err := findInProjectTree(target, childID, object, visited) + if err != nil { + return nil, err + } else if pathInProjectTree != nil { + return append(pathInProjectTree, treeNode), nil + } + } + return nil, nil +} + +func resolveFilePath(nodes []projectEntry) (string, error) { + var partialPath string + for _, entry := range nodes { + switch entry.pathRelation { + case groupParent: + partialPath = path.Join(entry.path, partialPath) + case absoluteParentPath: + return path.Join(entry.path, partialPath), nil + case undefinedParent: + case unsupportedParent: + return "", fmt.Errorf("failed to resolve path, unsupported path relation") + } + } + return partialPath, nil +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcodeproj/target.go b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/target.go new file mode 100644 index 0000000..18da34e --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/target.go @@ -0,0 +1,181 @@ +package xcodeproj + +import ( + "fmt" + "path/filepath" + + "github.com/bitrise-io/xcode-project/serialized" +) + +// TargetType ... +type TargetType string + +// TargetTypes +const ( + NativeTargetType TargetType = "PBXNativeTarget" + AggregateTargetType TargetType = "PBXAggregateTarget" + LegacyTargetType TargetType = "PBXLegacyTarget" +) + +// Target ... +type Target struct { + Type TargetType + ID string + Name string + BuildConfigurationList ConfigurationList + Dependencies []TargetDependency + ProductReference ProductReference + ProductType string + buildPhaseIDs []string +} + +// DependentTargets ... +func (t Target) DependentTargets() []Target { + var targets []Target + for _, targetDependency := range t.Dependencies { + childTarget := targetDependency.Target + targets = append(targets, childTarget) + + childDependentTargets := childTarget.DependentTargets() + targets = append(targets, childDependentTargets...) + } + + return targets +} + +// DependentExecutableProductTargets ... +func (t Target) DependentExecutableProductTargets(includeUITest bool) []Target { + var targets []Target + for _, targetDependency := range t.Dependencies { + childTarget := targetDependency.Target + if !childTarget.IsExecutableProduct() && (!includeUITest || !childTarget.IsUITestProduct()) { + continue + } + + targets = append(targets, childTarget) + + childDependentTargets := childTarget.DependentExecutableProductTargets(includeUITest) + targets = append(targets, childDependentTargets...) + } + + return targets +} + +// IsAppProduct ... +func (t Target) IsAppProduct() bool { + return filepath.Ext(t.ProductReference.Path) == ".app" +} + +// IsAppExtensionProduct ... +func (t Target) IsAppExtensionProduct() bool { + return filepath.Ext(t.ProductReference.Path) == ".appex" +} + +// IsExecutableProduct ... +func (t Target) IsExecutableProduct() bool { + return t.IsAppProduct() || t.IsAppExtensionProduct() +} + +// IsTestProduct ... +func (t Target) IsTestProduct() bool { + return filepath.Ext(t.ProductType) == ".unit-test" +} + +// IsUITestProduct ... +func (t Target) IsUITestProduct() bool { + return filepath.Ext(t.ProductType) == ".ui-testing" +} + +func parseTarget(id string, objects serialized.Object) (Target, error) { + rawTarget, err := objects.Object(id) + if err != nil { + return Target{}, err + } + + isa, err := rawTarget.String("isa") + if err != nil { + return Target{}, err + } + + var targetType TargetType + switch isa { + case "PBXNativeTarget": + targetType = NativeTargetType + case "PBXAggregateTarget": + targetType = AggregateTargetType + case "PBXLegacyTarget": + targetType = LegacyTargetType + default: + return Target{}, fmt.Errorf("unknown target type: %s", isa) + } + + name, err := rawTarget.String("name") + if err != nil { + return Target{}, err + } + + productType, err := rawTarget.String("productType") + if err != nil && !serialized.IsKeyNotFoundError(err) { + return Target{}, err + } + + buildConfigurationListID, err := rawTarget.String("buildConfigurationList") + if err != nil { + return Target{}, err + } + + buildConfigurationList, err := parseConfigurationList(buildConfigurationListID, objects) + if err != nil { + return Target{}, err + } + + dependencyIDs, err := rawTarget.StringSlice("dependencies") + if err != nil { + return Target{}, err + } + + var dependencies []TargetDependency + for _, dependencyID := range dependencyIDs { + dependency, err := parseTargetDependency(dependencyID, objects) + if err != nil { + // KeyNotFoundError can be only raised if the 'target' property not found on the raw target dependency object + // we only care about target dependency, which points to a target + if serialized.IsKeyNotFoundError(err) { + continue + } else { + return Target{}, err + } + } + + dependencies = append(dependencies, dependency) + } + + var productReference ProductReference + productReferenceID, err := rawTarget.String("productReference") + if err != nil { + if !serialized.IsKeyNotFoundError(err) { + return Target{}, err + } + } else { + productReference, err = parseProductReference(productReferenceID, objects) + if err != nil { + return Target{}, err + } + } + + buildPhaseIDs, err := rawTarget.StringSlice("buildPhases") + if err != nil { + return Target{}, err + } + + return Target{ + Type: targetType, + ID: id, + Name: name, + BuildConfigurationList: buildConfigurationList, + Dependencies: dependencies, + ProductReference: productReference, + ProductType: productType, + buildPhaseIDs: buildPhaseIDs, + }, nil +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcodeproj/target_dependency.go b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/target_dependency.go new file mode 100644 index 0000000..75db62a --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/target_dependency.go @@ -0,0 +1,31 @@ +package xcodeproj + +import "github.com/bitrise-io/xcode-project/serialized" + +// TargetDependency ... +type TargetDependency struct { + ID string + Target Target +} + +func parseTargetDependency(id string, objects serialized.Object) (TargetDependency, error) { + rawTargetDependency, err := objects.Object(id) + if err != nil { + return TargetDependency{}, err + } + + targetID, err := rawTargetDependency.String("target") + if err != nil { + return TargetDependency{}, err + } + + target, err := parseTarget(targetID, objects) + if err != nil { + return TargetDependency{}, err + } + + return TargetDependency{ + ID: id, + Target: target, + }, nil +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcodeproj/xcodeproj.go b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/xcodeproj.go new file mode 100644 index 0000000..b7478ef --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcodeproj/xcodeproj.go @@ -0,0 +1,490 @@ +package xcodeproj + +import ( + "errors" + "fmt" + "io/ioutil" + "path" + "path/filepath" + "regexp" + "strings" + + "github.com/bitrise-io/xcode-project/pretty" + + "github.com/bitrise-io/go-utils/fileutil" + "github.com/bitrise-io/go-utils/pathutil" + "github.com/bitrise-io/xcode-project/serialized" + "github.com/bitrise-io/xcode-project/xcodebuild" + "github.com/bitrise-io/xcode-project/xcscheme" + "golang.org/x/text/unicode/norm" + "howett.net/plist" +) + +// XcodeProj ... +type XcodeProj struct { + Proj Proj + RawProj serialized.Object + Format int + + Name string + Path string +} + +func (p XcodeProj) buildSettingsFilePath(target, configuration, key string) (string, error) { + buildSettings, err := p.TargetBuildSettings(target, configuration) + if err != nil { + return "", err + } + + pth, err := buildSettings.String(key) + if err != nil { + return "", err + } + + if pathutil.IsRelativePath(pth) { + pth = filepath.Join(filepath.Dir(p.Path), pth) + } + + return pth, nil +} + +// TargetCodeSignEntitlementsPath ... +func (p XcodeProj) TargetCodeSignEntitlementsPath(target, configuration string) (string, error) { + return p.buildSettingsFilePath(target, configuration, "CODE_SIGN_ENTITLEMENTS") +} + +// ForceTargetCodeSignEntitlement updates the project descriptor behind p. It +// searches for the entitlements file for target and configuration and sets the +// entitlement key to the value provided. +// Error is returned: +// - if there's an error during reading or writing the file +// - if the file does not exist for the given target and configuration +func (p XcodeProj) ForceTargetCodeSignEntitlement(target, configuration, entitlement string, value interface{}) error { + codeSignEntitlementsPth, err := p.TargetCodeSignEntitlementsPath(target, configuration) + if err != nil { + return err + } + + codeSignEntitlements, format, err := ReadPlistFile(codeSignEntitlementsPth) + if err != nil { + return err + } + + codeSignEntitlements[entitlement] = value + + return WritePlistFile(codeSignEntitlementsPth, codeSignEntitlements, format) +} + +// TargetCodeSignEntitlements ... +func (p XcodeProj) TargetCodeSignEntitlements(target, configuration string) (serialized.Object, error) { + codeSignEntitlementsPth, err := p.TargetCodeSignEntitlementsPath(target, configuration) + if err != nil { + return nil, err + } + + codeSignEntitlements, _, err := ReadPlistFile(codeSignEntitlementsPth) + if err != nil { + return nil, err + } + + return codeSignEntitlements, nil +} + +// TargetInformationPropertyListPath ... +func (p XcodeProj) TargetInformationPropertyListPath(target, configuration string) (string, error) { + return p.buildSettingsFilePath(target, configuration, "INFOPLIST_FILE") +} + +// TargetInformationPropertyList ... +func (p XcodeProj) TargetInformationPropertyList(target, configuration string) (serialized.Object, error) { + informationPropertyListPth, err := p.TargetInformationPropertyListPath(target, configuration) + if err != nil { + return nil, err + } + + informationPropertyListContent, err := fileutil.ReadBytesFromFile(informationPropertyListPth) + if err != nil { + return nil, err + } + + var informationPropertyList serialized.Object + if _, err := plist.Unmarshal([]byte(informationPropertyListContent), &informationPropertyList); err != nil { + return nil, err + } + + return informationPropertyList, nil +} + +// ForceTargetBundleID updates the projects bundle ID for the specified target +// and configuration. +// An error is returned if: +// - the target or configuration is not found +// - the given target or configuration is not found +func (p XcodeProj) ForceTargetBundleID(target, configuration, bundleID string) error { + t, targetFound := p.Proj.TargetByName(target) + if !targetFound { + return fmt.Errorf("could not find target (%s)", target) + } + + var configurationFound bool + buildConfigurations := t.BuildConfigurationList.BuildConfigurations + for _, c := range buildConfigurations { + if c.Name == configuration { + configurationFound = true + c.BuildSettings["PRODUCT_BUNDLE_IDENTIFIER"] = bundleID + } + } + + if !configurationFound { + return fmt.Errorf("could not find configuration (%s) for target (%s)", configuration, target) + } + + return p.Save() +} + +// TargetBundleID ... +func (p XcodeProj) TargetBundleID(target, configuration string) (string, error) { + buildSettings, err := p.TargetBuildSettings(target, configuration) + if err != nil { + return "", err + } + + bundleID, err := buildSettings.String("PRODUCT_BUNDLE_IDENTIFIER") + if err != nil && !serialized.IsKeyNotFoundError(err) { + return "", err + } + + if bundleID != "" { + return Resolve(bundleID, buildSettings) + } + + informationPropertyList, err := p.TargetInformationPropertyList(target, configuration) + if err != nil { + return "", err + } + + bundleID, err = informationPropertyList.String("CFBundleIdentifier") + if err != nil { + return "", err + } + + if bundleID == "" { + return "", errors.New("no PRODUCT_BUNDLE_IDENTIFIER build settings nor CFBundleIdentifier information property found") + } + + return Resolve(bundleID, buildSettings) +} + +// Resolve returns the resolved bundleID. We need this, because the bundleID is not exposed in the .pbxproj file ( raw ). +// If the raw BundleID contains an environment variable we have to replace it. +// +//**Example:** +//BundleID in the .pbxproj: Bitrise.Test.$(PRODUCT_NAME:rfc1034identifier).Suffix +//BundleID after the env is expanded: Bitrise.Test.Sample.Suffix +func Resolve(bundleID string, buildSettings serialized.Object) (string, error) { + resolvedBundleIDs := map[string]bool{} + resolved := bundleID + for true { + if !strings.Contains(resolved, "$") { + return resolved, nil + } + + var err error + resolved, err = expand(resolved, buildSettings) + if err != nil { + return "", err + } + + _, ok := resolvedBundleIDs[resolved] + if ok { + return "", fmt.Errorf("bundle id reference cycle found") + } + resolvedBundleIDs[resolved] = true + } + return "", fmt.Errorf("failed to resolve bundle id: %s", bundleID) +} + +func expand(bundleID string, buildSettings serialized.Object) (string, error) { + r, err := regexp.Compile("[$][{(][^$]*?[)}]") + if err != nil { + return "", err + } + if r.MatchString(bundleID) { + // envs like: $(PRODUCT_NAME:rfc1034identifier) || $(PRODUCT_NAME) || ${PRODUCT_NAME:rfc1034identifier} || ${PRODUCT_NAME} + return expandComplexEnv(bundleID, buildSettings) + } + // envs like: $PRODUCT_NAME + return expandSimpleEnv(bundleID, buildSettings) +} + +// expandComplexEnv expands the env with the "[$][{(][^$]*?[)}]" regex +// **Example:** `prefix.$(ENV_KEY:rfc1034identifier).suffix.$(ENV_KEY:rfc1034identifier)` **=>** `auto_provision.ios-simple-objc.suffix.ios-simple-objc` +func expandComplexEnv(bundleID string, buildSettings serialized.Object) (string, error) { + r, err := regexp.Compile("[$][{(][^$]*?[)}]") + if err != nil { + return "", err + } + rawEnvKey := r.FindString(bundleID) + + replacer := strings.NewReplacer("$", "", "(", "", ")", "", "{", "", "}", "") + envKey := strings.Split(replacer.Replace(rawEnvKey), ":")[0] + + envValue, ok := envInBuildSettings(envKey, buildSettings) + if !ok { + return "", fmt.Errorf("failed to find env in build settings: %s", envKey) + } + return strings.Replace(bundleID, rawEnvKey, envValue, -1), nil +} + +// expandSimpleEnv expands the env with the "[$][^$]*" regex +// **Example:** `prefix.$ENV_KEY.suffix.$ENV_KEY` **=>** `auto_provision.ios-simple-objc.suffix.ios-simple-objc` +func expandSimpleEnv(bundleID string, buildSettings serialized.Object) (string, error) { + r, err := regexp.Compile("[$][^$]*") + if err != nil { + return "", err + } + if !r.MatchString(bundleID) { + return "", fmt.Errorf("failed to match regex [$][^$]* for %s", bundleID) + } + envKey := r.FindString(bundleID) + + var envValue string + for len(envKey) > 1 { + var ok bool + envValue, ok = envInBuildSettings(strings.Replace(envKey, "$", "", 1), buildSettings) + if ok { + break + } + + envKey = envKey[:len(envKey)-1] + } + return strings.Replace(bundleID, envKey, envValue, -1), nil + +} + +func envInBuildSettings(envKey string, buildSettings serialized.Object) (string, bool) { + envValue, err := buildSettings.String(envKey) + if err != nil { + return "", false + } + return envValue, true +} + +// TargetBuildSettings ... +func (p XcodeProj) TargetBuildSettings(target, configuration string, customOptions ...string) (serialized.Object, error) { + return xcodebuild.ShowProjectBuildSettings(p.Path, target, configuration, customOptions...) +} + +// Scheme returns the project's scheme by name and the project's absolute path. +func (p XcodeProj) Scheme(name string) (*xcscheme.Scheme, string, error) { + schemes, err := p.Schemes() + if err != nil { + return nil, "", err + } + + normName := norm.NFC.String(name) + for _, scheme := range schemes { + if norm.NFC.String(scheme.Name) == normName { + return &scheme, p.Path, nil + } + } + + return nil, "", xcscheme.NotFoundError{Scheme: name, Container: p.Name} +} + +// Schemes ... +func (p XcodeProj) Schemes() ([]xcscheme.Scheme, error) { + return xcscheme.FindSchemesIn(p.Path) +} + +// Open ... +func Open(pth string) (XcodeProj, error) { + absPth, err := pathutil.AbsPath(pth) + if err != nil { + return XcodeProj{}, err + } + + format, raw, objects, projectID, err := open(pth) + if err != nil { + return XcodeProj{}, err + } + + p, err := parseProj(projectID, objects) + if err != nil { + return XcodeProj{}, err + } + + return XcodeProj{ + Proj: p, + RawProj: raw, + Format: format, + Path: absPth, + Name: strings.TrimSuffix(filepath.Base(absPth), filepath.Ext(absPth)), + }, nil +} + +// open parse the provided .pbxprog file. +// Returns the `raw` contents as a serialized.Object, the `objects` as serialized.Object and the PBXProject's `projectID` as string +// If there was an error during the parsing it returns an error +func open(absPth string) (format int, rawPbxProj serialized.Object, objects serialized.Object, projectID string, err error) { + pbxProjPth := filepath.Join(absPth, "project.pbxproj") + + var b []byte + b, err = fileutil.ReadBytesFromFile(pbxProjPth) + if err != nil { + return + } + + if format, err = plist.Unmarshal(b, &rawPbxProj); err != nil { + err = fmt.Errorf("failed to generate json from Pbxproj - error: %s", err) + return + } + + objects, err = rawPbxProj.Object("objects") + if err != nil { + return + } + + for id := range objects { + var object serialized.Object + object, err = objects.Object(id) + if err != nil { + return + } + + var objectISA string + objectISA, err = object.String("isa") + if err != nil { + return + } + + if objectISA == "PBXProject" { + projectID = id + break + } + } + return +} + +// IsXcodeProj ... +func IsXcodeProj(pth string) bool { + return filepath.Ext(pth) == ".xcodeproj" +} + +// ForceCodeSign modifies the project's code signing settings to use manual code signing. +// +// Overrides the target's `ProvisioningStyle`, `DevelopmentTeam` and clears the `DevelopmentTeamName` in the **TargetAttributes**. +// Overrides the target's `CODE_SIGN_STYLE`, `DEVELOPMENT_TEAM`, `CODE_SIGN_IDENTITY`, `CODE_SIGN_IDENTITY[sdk=iphoneos*]` `PROVISIONING_PROFILE_SPECIFIER`, +// `PROVISIONING_PROFILE` and `PROVISIONING_PROFILE[sdk=iphoneos*]` in the **BuildSettings**. +func (p *XcodeProj) ForceCodeSign(configuration, targetName, developmentTeam, codesignIdentity, provisioningProfileUUID string) error { + target, ok := p.Proj.TargetByName(targetName) + if !ok { + return fmt.Errorf("failed to find target with name: %s", targetName) + } + + targetAttributes, err := p.TargetAttributes() + if err != nil { + return fmt.Errorf("failed to get project's target attributes, error: %s", err) + } + + buildConfigurationList, err := p.BuildConfigurationList(target.ID) + if err != nil { + return fmt.Errorf("failed to get target's (%s) buildConfigurationList, error: %s", target.ID, err) + } + buildConfigurations, err := p.BuildConfigurations(buildConfigurationList) + if err != nil { + return fmt.Errorf("failed to get buildConfigurations of buildConfigurationList (%s), error: %s", pretty.Object(buildConfigurationList), err) + } + + var buildConfiguration serialized.Object + for _, b := range buildConfigurations { + if b["name"] == configuration { + buildConfiguration = b + break + } + } + + if buildConfiguration == nil { + return fmt.Errorf("failed to find buildConfiguration for configuration %s in the buildConfiguration list: %s", configuration, pretty.Object(buildConfigurations)) + } + + // Override TargetAttributes + if err = forceCodeSignOnTargetAttributes(targetAttributes, target.ID, developmentTeam); err != nil { + return fmt.Errorf("failed to change code signing in target attributes, error: %s", err) + } + + // Override BuildSettings + if err = forceCodeSignOnBuildConfiguration(buildConfiguration, developmentTeam, provisioningProfileUUID, codesignIdentity); err != nil { + return fmt.Errorf("failed to change code signing in build settings, error: %s", err) + } + return nil +} + +// forceCodeSignOnTargetAttributes sets the TargetAttributes for the provided targetID. +// **Overrides the ProvisioningStyle, developmentTeam and clears the DevelopmentTeamName in the provided `targetAttributes`!** +func forceCodeSignOnTargetAttributes(targetAttributes serialized.Object, targetID, developmentTeam string) error { + targetAttribute, err := targetAttributes.Object(targetID) + if err != nil { + return fmt.Errorf("failed to get traget's (%s) attributes, error: %s", targetID, err) + } + + targetAttribute["ProvisioningStyle"] = "Manual" + targetAttribute["DevelopmentTeam"] = developmentTeam + targetAttribute["DevelopmentTeamName"] = "" + return nil +} + +// forceCodeSignOnBuildConfiguration sets the BuildSettings for the provided build configuration. +// **Overrides the CODE_SIGN_STYLE, DEVELOPMENT_TEAM, CODE_SIGN_IDENTITY, PROVISIONING_PROFILE +// and clears the PROVISIONING_PROFILE_SPECIFIER in the provided `buildConfiguration`, +// each modification also applies for the sdk specific settings too (CODE_SIGN_IDENTITY[sdk=iphoneos*])!** +func forceCodeSignOnBuildConfiguration(buildConfiguration serialized.Object, developmentTeam, provisioningProfileUUID, codesignIdentity string) error { + buildSettings, err := buildConfiguration.Object("buildSettings") + if err != nil { + return fmt.Errorf("failed to get buildSettings of buildConfiguration (%s), error: %s", pretty.Object(buildConfiguration), err) + } + + forceAttributes := map[string]string{ + "CODE_SIGN_STYLE": "Manual", + "DEVELOPMENT_TEAM": developmentTeam, + "CODE_SIGN_IDENTITY": codesignIdentity, + "PROVISIONING_PROFILE_SPECIFIER": "", + "PROVISIONING_PROFILE": provisioningProfileUUID, + } + for key, value := range forceAttributes { + writeAttributeForAllSDKs(buildSettings, key, value) + } + + return nil +} + +func writeAttributeForAllSDKs(buildSettings serialized.Object, newKey string, newValue string) { + buildSettings[newKey] = newValue + + // override specific build setting if any: https://stackoverflow.com/a/5382708/5842489 + // Example: CODE_SIGN_IDENTITY[sdk=iphoneos*] + matcher := regexp.MustCompile(fmt.Sprintf(`^%s\[sdk=.*\]$`, regexp.QuoteMeta(newKey))) + for oldKey := range buildSettings { + if matcher.Match([]byte(oldKey)) { + buildSettings[oldKey] = newValue + } + } +} + +// Save the XcodeProj +// +// Overrides the project.pbxproj file of the XcodeProj with the contents of `rawProj` +func (p XcodeProj) Save() error { + return p.savePBXProj() +} + +// savePBXProj overrides the project.pbxproj file of the XcodeProj with the contents of `rawProj` +func (p XcodeProj) savePBXProj() error { + b, err := plist.Marshal(p.RawProj, p.Format) + if err != nil { + return fmt.Errorf("failed to marshal .pbxproj") + } + + pth := path.Join(p.Path, "project.pbxproj") + return ioutil.WriteFile(pth, b, 0644) +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcscheme/errors.go b/vendor/github.com/bitrise-io/xcode-project/xcscheme/errors.go new file mode 100644 index 0000000..4ea9ba8 --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcscheme/errors.go @@ -0,0 +1,23 @@ +package xcscheme + +import "fmt" + +// NotFoundError represents that the given scheme was not found in the container +type NotFoundError struct { + Scheme string + Container string +} + +// Error implements the error interface +func (e NotFoundError) Error() string { + return fmt.Sprintf("scheme %s not found in %s", e.Scheme, e.Container) +} + +// IsNotFoundError reports whatever the given error is an instance of NotFoundError +func IsNotFoundError(err error) bool { + if err == nil { + return false + } + _, ok := err.(NotFoundError) + return ok +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcscheme/util.go b/vendor/github.com/bitrise-io/xcode-project/xcscheme/util.go new file mode 100644 index 0000000..804b443 --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcscheme/util.go @@ -0,0 +1,36 @@ +package xcscheme + +import ( + "path/filepath" +) + +// FindSchemesIn ... +func FindSchemesIn(root string) (schemes []Scheme, err error) { + // + // Add the shared schemes to the list + sharedPths, err := pathsByPattern(root, "xcshareddata", "xcschemes", "*.xcscheme") + if err != nil { + return nil, err + } + + // + // Add the non-shared user schemes to the list + userPths, err := pathsByPattern(root, "xcuserdata", "*.xcuserdatad", "xcschemes", "*.xcscheme") + if err != nil { + return nil, err + } + + for _, pth := range append(sharedPths, userPths...) { + scheme, err := Open(pth) + if err != nil { + return nil, err + } + schemes = append(schemes, scheme) + } + return +} + +func pathsByPattern(paths ...string) ([]string, error) { + pattern := filepath.Join(paths...) + return filepath.Glob(pattern) +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcscheme/xcscheme.go b/vendor/github.com/bitrise-io/xcode-project/xcscheme/xcscheme.go new file mode 100644 index 0000000..bbc11fd --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcscheme/xcscheme.go @@ -0,0 +1,110 @@ +package xcscheme + +import ( + "encoding/xml" + "fmt" + "path/filepath" + "strings" + + "github.com/bitrise-io/go-utils/fileutil" + "github.com/bitrise-io/go-utils/pathutil" +) + +// BuildableReference ... +type BuildableReference struct { + BlueprintIdentifier string `xml:"BlueprintIdentifier,attr"` + BlueprintName string `xml:"BlueprintName,attr"` + BuildableName string `xml:"BuildableName,attr"` + ReferencedContainer string `xml:"ReferencedContainer,attr"` +} + +// IsAppReference ... +func (r BuildableReference) IsAppReference() bool { + return filepath.Ext(r.BuildableName) == ".app" +} + +// ReferencedContainerAbsPath ... +func (r BuildableReference) ReferencedContainerAbsPath(schemeContainerDir string) (string, error) { + s := strings.Split(r.ReferencedContainer, ":") + if len(s) != 2 { + return "", fmt.Errorf("unknown referenced container (%s)", r.ReferencedContainer) + } + + base := s[1] + absPth := filepath.Join(schemeContainerDir, base) + return pathutil.AbsPath(absPth) +} + +// BuildActionEntry ... +type BuildActionEntry struct { + BuildForTesting string `xml:"buildForTesting,attr"` + BuildForArchiving string `xml:"buildForArchiving,attr"` + BuildableReference BuildableReference +} + +// BuildAction ... +type BuildAction struct { + BuildActionEntries []BuildActionEntry `xml:"BuildActionEntries>BuildActionEntry"` +} + +// TestableReference ... +type TestableReference struct { + Skipped string `xml:"skipped,attr"` + BuildableReference BuildableReference +} + +// TestAction ... +type TestAction struct { + Testables []TestableReference `xml:"Testables>TestableReference"` + BuildConfiguration string `xml:"buildConfiguration,attr"` +} + +// ArchiveAction ... +type ArchiveAction struct { + BuildConfiguration string `xml:"buildConfiguration,attr"` +} + +// Scheme ... +type Scheme struct { + BuildAction BuildAction + ArchiveAction ArchiveAction + TestAction TestAction + + Name string + Path string +} + +// Open ... +func Open(pth string) (Scheme, error) { + b, err := fileutil.ReadBytesFromFile(pth) + if err != nil { + return Scheme{}, err + } + + var scheme Scheme + if err := xml.Unmarshal(b, &scheme); err != nil { + return Scheme{}, fmt.Errorf("failed to unmarshal scheme file: %s, error: %s", pth, err) + } + + scheme.Name = strings.TrimSuffix(filepath.Base(pth), filepath.Ext(pth)) + scheme.Path = pth + + return scheme, nil +} + +// AppBuildActionEntry ... +func (s Scheme) AppBuildActionEntry() (BuildActionEntry, bool) { + var entry BuildActionEntry + for _, e := range s.BuildAction.BuildActionEntries { + if e.BuildForArchiving != "YES" { + continue + } + if !e.BuildableReference.IsAppReference() { + continue + } + entry = e + break + } + + return entry, (entry.BuildableReference.BlueprintIdentifier != "") +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcworkspace/file_ref.go b/vendor/github.com/bitrise-io/xcode-project/xcworkspace/file_ref.go new file mode 100644 index 0000000..c22b816 --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcworkspace/file_ref.go @@ -0,0 +1,61 @@ +package xcworkspace + +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/bitrise-io/go-utils/pathutil" +) + +// FileRef ... +type FileRef struct { + Location string `xml:"location,attr"` +} + +// FileRefType ... +type FileRefType string + +// Known FileRefTypes +const ( + AbsoluteFileRefType FileRefType = "absolute" + GroupFileRefType FileRefType = "group" + ContainerFileRefType FileRefType = "container" +) + +// TypeAndPath ... +func (f FileRef) TypeAndPath() (FileRefType, string, error) { + s := strings.Split(f.Location, ":") + if len(s) != 2 { + return "", "", fmt.Errorf("unknown file reference location (%s)", f.Location) + } + + switch s[0] { + case "absolute": + return AbsoluteFileRefType, s[1], nil + case "group": + return GroupFileRefType, s[1], nil + case "container": + return ContainerFileRefType, s[1], nil + default: + return "", "", fmt.Errorf("unknown file reference type: %s", s[0]) + } +} + +// AbsPath ... +func (f FileRef) AbsPath(dir string) (string, error) { + t, pth, err := f.TypeAndPath() + if err != nil { + return "", err + } + + var absPth string + switch t { + case AbsoluteFileRefType: + absPth = pth + case GroupFileRefType, ContainerFileRefType: + absPth = filepath.Join(dir, pth) + } + + return pathutil.AbsPath(absPth) +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcworkspace/group.go b/vendor/github.com/bitrise-io/xcode-project/xcworkspace/group.go new file mode 100644 index 0000000..725ad6a --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcworkspace/group.go @@ -0,0 +1,54 @@ +package xcworkspace + +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/bitrise-io/go-utils/pathutil" +) + +// Group ... +type Group struct { + Location string `xml:"location,attr"` + FileRefs []FileRef `xml:"FileRef"` + Groups []Group `xml:"Group"` +} + +// AbsPath ... +func (g Group) AbsPath(dir string) (string, error) { + s := strings.Split(g.Location, ":") + if len(s) != 2 { + return "", fmt.Errorf("unknown group location (%s)", g.Location) + } + pth := filepath.Join(dir, s[1]) + return pathutil.AbsPath(pth) +} + +// FileLocations ... +func (g Group) FileLocations(dir string) ([]string, error) { + var fileLocations []string + + groupPth, err := g.AbsPath(dir) + if err != nil { + return nil, err + } + + for _, fileRef := range g.FileRefs { + fileLocation, err := fileRef.AbsPath(groupPth) + if err != nil { + return nil, err + } + fileLocations = append(fileLocations, fileLocation) + } + + for _, group := range g.Groups { + groupFileLocations, err := group.FileLocations(groupPth) + if err != nil { + return nil, err + } + fileLocations = append(fileLocations, groupFileLocations...) + } + + return fileLocations, nil +} diff --git a/vendor/github.com/bitrise-io/xcode-project/xcworkspace/xcworkspace.go b/vendor/github.com/bitrise-io/xcode-project/xcworkspace/xcworkspace.go new file mode 100644 index 0000000..ea367ec --- /dev/null +++ b/vendor/github.com/bitrise-io/xcode-project/xcworkspace/xcworkspace.go @@ -0,0 +1,154 @@ +package xcworkspace + +import ( + "encoding/xml" + "fmt" + "path/filepath" + "strings" + + "github.com/bitrise-io/go-utils/fileutil" + "github.com/bitrise-io/go-utils/pathutil" + "github.com/bitrise-io/xcode-project/serialized" + "github.com/bitrise-io/xcode-project/xcodebuild" + "github.com/bitrise-io/xcode-project/xcodeproj" + "github.com/bitrise-io/xcode-project/xcscheme" + "golang.org/x/text/unicode/norm" +) + +// Workspace represents an Xcode workspace +type Workspace struct { + FileRefs []FileRef `xml:"FileRef"` + Groups []Group `xml:"Group"` + + Name string + Path string +} + +// Scheme returns the scheme by name and it's container's absolute path. +func (w Workspace) Scheme(name string) (*xcscheme.Scheme, string, error) { + schemesByContainer, err := w.Schemes() + if err != nil { + return nil, "", err + } + + normName := norm.NFC.String(name) + for container, schemes := range schemesByContainer { + for _, scheme := range schemes { + if norm.NFC.String(scheme.Name) == normName { + return &scheme, container, nil + } + } + } + + return nil, "", xcscheme.NotFoundError{Scheme: name, Container: w.Name} +} + +// SchemeBuildSettings ... +func (w Workspace) SchemeBuildSettings(scheme, configuration string, customOptions ...string) (serialized.Object, error) { + return xcodebuild.ShowWorkspaceBuildSettings(w.Path, scheme, configuration, customOptions...) +} + +// Schemes ... +func (w Workspace) Schemes() (map[string][]xcscheme.Scheme, error) { + schemesByContainer := map[string][]xcscheme.Scheme{} + + workspaceSchemes, err := xcscheme.FindSchemesIn(w.Path) + if err != nil { + return nil, err + } + + schemesByContainer[w.Path] = workspaceSchemes + + // project schemes + projectLocations, err := w.ProjectFileLocations() + if err != nil { + return nil, err + } + + for _, projectLocation := range projectLocations { + if exist, err := pathutil.IsPathExists(projectLocation); err != nil { + return nil, fmt.Errorf("failed to check if project exist at: %s, error: %s", projectLocation, err) + } else if !exist { + // at this point we are interested the schemes visible for the workspace + continue + } + + project, err := xcodeproj.Open(projectLocation) + if err != nil { + return nil, err + } + + projectSchemes, err := project.Schemes() + if err != nil { + return nil, err + } + + schemesByContainer[project.Path] = projectSchemes + } + + return schemesByContainer, nil +} + +// FileLocations ... +func (w Workspace) FileLocations() ([]string, error) { + var fileLocations []string + + for _, fileRef := range w.FileRefs { + pth, err := fileRef.AbsPath(filepath.Dir(w.Path)) + if err != nil { + return nil, err + } + + fileLocations = append(fileLocations, pth) + } + + for _, group := range w.Groups { + groupFileLocations, err := group.FileLocations(filepath.Dir(w.Path)) + if err != nil { + return nil, err + } + + fileLocations = append(fileLocations, groupFileLocations...) + } + + return fileLocations, nil +} + +// ProjectFileLocations ... +func (w Workspace) ProjectFileLocations() ([]string, error) { + var projectLocations []string + fileLocations, err := w.FileLocations() + if err != nil { + return nil, err + } + for _, fileLocation := range fileLocations { + if xcodeproj.IsXcodeProj(fileLocation) { + projectLocations = append(projectLocations, fileLocation) + } + } + return projectLocations, nil +} + +// Open ... +func Open(pth string) (Workspace, error) { + contentsPth := filepath.Join(pth, "contents.xcworkspacedata") + b, err := fileutil.ReadBytesFromFile(contentsPth) + if err != nil { + return Workspace{}, err + } + + var workspace Workspace + if err := xml.Unmarshal(b, &workspace); err != nil { + return Workspace{}, fmt.Errorf("failed to unmarshal workspace file: %s, error: %s", pth, err) + } + + workspace.Name = strings.TrimSuffix(filepath.Base(pth), filepath.Ext(pth)) + workspace.Path = pth + + return workspace, nil +} + +// IsWorkspace ... +func IsWorkspace(pth string) bool { + return filepath.Ext(pth) == ".xcworkspace" +} diff --git a/vendor/github.com/bitrise-steplib/steps-xcode-archive/LICENSE b/vendor/github.com/bitrise-steplib/steps-xcode-archive/LICENSE new file mode 100644 index 0000000..a6a5c39 --- /dev/null +++ b/vendor/github.com/bitrise-steplib/steps-xcode-archive/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Bitrise + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/code_sign_mapping.go b/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/code_sign_mapping.go new file mode 100644 index 0000000..be2783e --- /dev/null +++ b/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/code_sign_mapping.go @@ -0,0 +1,118 @@ +package utils + +import ( + "fmt" + "sort" + + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-io/go-xcode/certificateutil" + "github.com/bitrise-io/go-xcode/exportoptions" + "github.com/bitrise-io/go-xcode/profileutil" + "github.com/ryanuber/go-glob" +) + +// CodeSignGroupItem ... +type CodeSignGroupItem struct { + Certificate certificateutil.CertificateInfoModel + BundleIDProfileMap map[string]profileutil.ProvisioningProfileInfoModel +} + +func isCertificateInstalled(installedCertificates []certificateutil.CertificateInfoModel, certificate certificateutil.CertificateInfoModel) bool { + installed := false + for _, installedCertificate := range installedCertificates { + if certificate.Serial == installedCertificate.Serial { + installed = true + break + } + } + + if installed { + log.Printf("certificate: %s [%s] is installed", certificate.CommonName, certificate.Serial) + } + + return installed +} + +func createCertificateProfilesMapping(profiles []profileutil.ProvisioningProfileInfoModel, certificates []certificateutil.CertificateInfoModel) map[string][]profileutil.ProvisioningProfileInfoModel { + createCertificateProfilesMap := map[string][]profileutil.ProvisioningProfileInfoModel{} + for _, profile := range profiles { + for _, embeddedCert := range profile.DeveloperCertificates { + if !isCertificateInstalled(certificates, embeddedCert) { + continue + } + + if _, ok := createCertificateProfilesMap[embeddedCert.Serial]; !ok { + createCertificateProfilesMap[embeddedCert.Serial] = []profileutil.ProvisioningProfileInfoModel{} + } + createCertificateProfilesMap[embeddedCert.Serial] = append(createCertificateProfilesMap[embeddedCert.Serial], profile) + } + } + + fmt.Println() + + for subject, profiles := range createCertificateProfilesMap { + log.Printf("certificate: %s included in profiles:", subject) + for _, profile := range profiles { + log.Printf("- %s", profile.Name) + } + fmt.Println() + } + + return createCertificateProfilesMap +} + +func createCodeSignGroups(profileGroups map[string][]profileutil.ProvisioningProfileInfoModel, bundleIDs []string, exportMethod exportoptions.Method) []CodeSignGroupItem { + filteredCodeSignGroupItems := []CodeSignGroupItem{} + for groupItemCertificateSerial, profiles := range profileGroups { + log.Printf("checking certificate (%s) group:", groupItemCertificateSerial) + sort.Sort(ByBundleIDLength(profiles)) + + bundleIDProfileMap := map[string]profileutil.ProvisioningProfileInfoModel{} + for _, bundleID := range bundleIDs { + for _, profile := range profiles { + if profile.ExportType != exportMethod { + log.Printf("profile (%s) export method (%s) is not the desired (%s)", profile.Name, profile.ExportType, exportMethod) + continue + } + + if !glob.Glob(profile.BundleID, bundleID) { + log.Printf("profile (%s) does not provision bundle id: %s", profile.Name, profile.BundleID) + continue + } + + log.Printf("profile (%s) MATCHES for bundle id (%s) and export method (%s)", profile.Name, bundleID, exportMethod) + + bundleIDProfileMap[bundleID] = profile + break + } + } + + log.Printf("matching profiles: %d should be: %d", len(bundleIDProfileMap), len(bundleIDs)) + fmt.Println() + + if len(bundleIDProfileMap) == len(bundleIDs) { + groupItemCertificate := certificateutil.CertificateInfoModel{} + for _, profile := range bundleIDProfileMap { + for _, certificate := range profile.DeveloperCertificates { + if groupItemCertificateSerial == certificate.Serial { + groupItemCertificate = certificate + } + } + } + + filteredCodeSignGroupItems = append(filteredCodeSignGroupItems, CodeSignGroupItem{Certificate: groupItemCertificate, BundleIDProfileMap: bundleIDProfileMap}) + } + } + return filteredCodeSignGroupItems +} + +// ResolveCodeSignGroupItems ... +func ResolveCodeSignGroupItems(bundleIDs []string, exportMethod exportoptions.Method, profiles []profileutil.ProvisioningProfileInfoModel, certificates []certificateutil.CertificateInfoModel) []CodeSignGroupItem { + log.Printf("Creating certificate profiles mapping...") + certificateProfilesMapping := createCertificateProfilesMapping(profiles, certificates) + + log.Printf("Creating CodeSignGroups...") + groups := createCodeSignGroups(certificateProfilesMapping, bundleIDs, exportMethod) + + return groups +} diff --git a/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/entitlements.go b/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/entitlements.go new file mode 100644 index 0000000..53e1f91 --- /dev/null +++ b/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/entitlements.go @@ -0,0 +1,52 @@ +package utils + +import ( + "fmt" + + "github.com/bitrise-io/xcode-project/xcscheme" + + "github.com/bitrise-io/go-xcode/plistutil" + "github.com/bitrise-io/xcode-project/serialized" + "github.com/bitrise-io/xcode-project/xcodeproj" +) + +// ProjectEntitlementsByBundleID ... +func ProjectEntitlementsByBundleID(xcodeProj *xcodeproj.XcodeProj, scheme *xcscheme.Scheme, configurationName string) (map[string]plistutil.PlistData, error) { + archiveEntry, ok := scheme.AppBuildActionEntry() + if !ok { + return nil, fmt.Errorf("archivable entry not found in project: %s, scheme: %s", xcodeProj.Path, scheme.Name) + } + + mainTarget, ok := xcodeProj.Proj.Target(archiveEntry.BuildableReference.BlueprintIdentifier) + if !ok { + return nil, fmt.Errorf("target not found: %s", archiveEntry.BuildableReference.BlueprintIdentifier) + } + + targets := append([]xcodeproj.Target{mainTarget}, mainTarget.DependentExecutableProductTargets(false)...) + + entitlementsByBundleID := map[string]serialized.Object{} + + for _, target := range targets { + bundleID, err := xcodeProj.TargetBundleID(target.Name, configurationName) + if err != nil { + return nil, fmt.Errorf("failed to get target (%s) bundle id: %s", target.Name, err) + } + + entitlements, err := xcodeProj.TargetCodeSignEntitlements(target.Name, configurationName) + if err != nil && !serialized.IsKeyNotFoundError(err) { + return nil, fmt.Errorf("failed to get target (%s) bundle id: %s", target.Name, err) + } + + entitlementsByBundleID[bundleID] = entitlements + } + + return toMapStringPlistData(entitlementsByBundleID), nil +} + +func toMapStringPlistData(object map[string]serialized.Object) map[string]plistutil.PlistData { + converted := map[string]plistutil.PlistData{} + for key, value := range object { + converted[key] = plistutil.PlistData(value) + } + return converted +} diff --git a/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/export.go b/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/export.go new file mode 100644 index 0000000..47cb848 --- /dev/null +++ b/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/export.go @@ -0,0 +1,78 @@ +package utils + +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/bitrise-io/go-utils/command" + "github.com/bitrise-io/go-utils/fileutil" + "github.com/bitrise-io/go-utils/pathutil" +) + +func zip(sourceDir, destinationZipPth string) error { + parentDir := filepath.Dir(sourceDir) + dirName := filepath.Base(sourceDir) + cmd := command.New("/usr/bin/zip", "-rTy", destinationZipPth, dirName) + cmd.SetDir(parentDir) + out, err := cmd.RunAndReturnTrimmedCombinedOutput() + if err != nil { + return fmt.Errorf("Failed to zip dir: %s, output: %s, error: %s", sourceDir, out, err) + } + + return nil +} + +func exportEnvironmentWithEnvman(keyStr, valueStr string) error { + cmd := command.New("envman", "add", "--key", keyStr) + cmd.SetStdin(strings.NewReader(valueStr)) + return cmd.Run() +} + +// ExportOutputDir ... +func ExportOutputDir(sourceDirPth, destinationDirPth, envKey string) error { + if sourceDirPth != destinationDirPth { + if err := command.CopyDir(sourceDirPth, destinationDirPth, true); err != nil { + return err + } + } + + return exportEnvironmentWithEnvman(envKey, destinationDirPth) +} + +// ExportOutputFile ... +func ExportOutputFile(sourcePth, destinationPth, envKey string) error { + if sourcePth != destinationPth { + if err := command.CopyFile(sourcePth, destinationPth); err != nil { + return err + } + } + + return exportEnvironmentWithEnvman(envKey, destinationPth) +} + +// ExportOutputFileContent ... +func ExportOutputFileContent(content, destinationPth, envKey string) error { + if err := fileutil.WriteStringToFile(destinationPth, content); err != nil { + return err + } + + return ExportOutputFile(destinationPth, destinationPth, envKey) +} + +// ExportOutputDirAsZip ... +func ExportOutputDirAsZip(sourceDirPth, destinationPth, envKey string) error { + tmpDir, err := pathutil.NormalizedOSTempDirPath("__export_tmp_dir__") + if err != nil { + return err + } + + base := filepath.Base(sourceDirPth) + tmpZipFilePth := filepath.Join(tmpDir, base+".zip") + + if err := zip(sourceDirPth, tmpZipFilePth); err != nil { + return err + } + + return ExportOutputFile(tmpZipFilePth, destinationPth, envKey) +} diff --git a/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/platform.go b/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/platform.go new file mode 100644 index 0000000..309cf28 --- /dev/null +++ b/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/platform.go @@ -0,0 +1,138 @@ +package utils + +import ( + "fmt" + "path/filepath" + "strings" + + project "github.com/bitrise-io/xcode-project" + "github.com/bitrise-io/xcode-project/serialized" + "github.com/bitrise-io/xcode-project/xcodeproj" + "github.com/bitrise-io/xcode-project/xcscheme" +) + +// Platform ... +type Platform string + +const ( + iOS Platform = "iOS" + osX Platform = "OS X" + tvOS Platform = "tvOS" + watchOS Platform = "watchOS" +) + +// OpenArchivableProject ... +func OpenArchivableProject(pth, schemeName, configurationName string) (*xcodeproj.XcodeProj, *xcscheme.Scheme, string, error) { + scheme, schemeContainerDir, err := project.Scheme(pth, schemeName) + if err != nil { + return nil, nil, "", fmt.Errorf("could not get scheme (%s) from path (%s): %s", schemeName, pth, err) + } + if configurationName == "" { + configurationName = scheme.ArchiveAction.BuildConfiguration + } + + if configurationName == "" { + return nil, nil, "", fmt.Errorf("no configuration provided nor default defined for the scheme's (%s) archive action", schemeName) + } + + archiveEntry, ok := scheme.AppBuildActionEntry() + if !ok { + return nil, nil, "", fmt.Errorf("archivable entry not found") + } + + projectPth, err := archiveEntry.BuildableReference.ReferencedContainerAbsPath(filepath.Dir(schemeContainerDir)) + if err != nil { + return nil, nil, "", err + } + + xcodeProj, err := xcodeproj.Open(projectPth) + if err != nil { + return nil, nil, "", err + } + return &xcodeProj, scheme, configurationName, nil +} + +// TargetBuildSettingsProvider ... +type TargetBuildSettingsProvider interface { + TargetBuildSettings(xcodeProj *xcodeproj.XcodeProj, target, configuration string, customOptions ...string) (serialized.Object, error) +} + +// XcodeBuild ... +type XcodeBuild struct { +} + +// TargetBuildSettings ... +func (x XcodeBuild) TargetBuildSettings(xcodeProj *xcodeproj.XcodeProj, target, configuration string, customOptions ...string) (serialized.Object, error) { + return xcodeProj.TargetBuildSettings(target, configuration, customOptions...) +} + +// BuildableTargetPlatform ... +func BuildableTargetPlatform( + xcodeProj *xcodeproj.XcodeProj, + scheme *xcscheme.Scheme, + configurationName string, + provider TargetBuildSettingsProvider, +) (Platform, error) { + archiveEntry, ok := scheme.AppBuildActionEntry() + if !ok { + return "", fmt.Errorf("archivable entry not found in project: %s, scheme: %s", xcodeProj.Path, scheme.Name) + } + + mainTarget, ok := xcodeProj.Proj.Target(archiveEntry.BuildableReference.BlueprintIdentifier) + if !ok { + return "", fmt.Errorf("target not found: %s", archiveEntry.BuildableReference.BlueprintIdentifier) + } + + settings, err := provider.TargetBuildSettings(xcodeProj, mainTarget.Name, configurationName) + if err != nil { + return "", fmt.Errorf("failed to get target (%s) build settings: %s", mainTarget.Name, err) + } + + return getPlatform(settings) +} + +func getPlatform(buildSettings serialized.Object) (Platform, error) { + /* + Xcode help: + Base SDK (SDKROOT) + The name or path of the base SDK being used during the build. + The product will be built against the headers and libraries located inside the indicated SDK. + This path will be prepended to all search paths, and will be passed through the environment to the compiler and linker. + Additional SDKs can be specified in the Additional SDKs (ADDITIONAL_SDKS) setting. + + Examples: + - /Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk + - /Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator13.4.sdk + - /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.4.sdk + - /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk + - /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk + - /Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS.sdk + - /Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform/Developer/SDKs/WatchSimulator.sdk + - iphoneos + - macosx + - appletvos + - watchos + */ + sdk, err := buildSettings.String("SDKROOT") + if err != nil { + return "", fmt.Errorf("failed to get SDKROOT: %s", err) + } + + sdk = strings.ToLower(sdk) + if filepath.Ext(sdk) == ".sdk" { + sdk = filepath.Base(sdk) + } + + switch { + case strings.HasPrefix(sdk, "iphoneos"): + return iOS, nil + case strings.HasPrefix(sdk, "macosx"): + return osX, nil + case strings.HasPrefix(sdk, "appletvos"): + return tvOS, nil + case strings.HasPrefix(sdk, "watchos"): + return watchOS, nil + default: + return "", fmt.Errorf("unkown SDKROOT: %s", sdk) + } +} diff --git a/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/profile.go b/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/profile.go new file mode 100644 index 0000000..97294fa --- /dev/null +++ b/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/profile.go @@ -0,0 +1,57 @@ +package utils + +import ( + "io" + "net/http" + "os" + "path/filepath" + + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-io/go-utils/pathutil" + "github.com/bitrise-io/go-xcode/profileutil" +) + +// GetDefaultProvisioningProfile ... +func GetDefaultProvisioningProfile() (profileutil.ProvisioningProfileInfoModel, error) { + defaultProfileURL := os.Getenv("BITRISE_DEFAULT_PROVISION_URL") + if defaultProfileURL == "" { + return profileutil.ProvisioningProfileInfoModel{}, nil + } + + tmpDir, err := pathutil.NormalizedOSTempDirPath("tmp_default_profile") + if err != nil { + return profileutil.ProvisioningProfileInfoModel{}, err + } + + tmpDst := filepath.Join(tmpDir, "default.mobileprovision") + tmpDstFile, err := os.Create(tmpDst) + if err != nil { + return profileutil.ProvisioningProfileInfoModel{}, err + } + defer func() { + if err := tmpDstFile.Close(); err != nil { + log.Errorf("Failed to close file (%s), error: %s", tmpDst, err) + } + }() + + response, err := http.Get(defaultProfileURL) + if err != nil { + return profileutil.ProvisioningProfileInfoModel{}, err + } + defer func() { + if err := response.Body.Close(); err != nil { + log.Errorf("Failed to close response body, error: %s", err) + } + }() + + if _, err := io.Copy(tmpDstFile, response.Body); err != nil { + return profileutil.ProvisioningProfileInfoModel{}, err + } + + defaultProfile, err := profileutil.NewProvisioningProfileInfoFromFile(tmpDst) + if err != nil { + return profileutil.ProvisioningProfileInfoModel{}, err + } + + return defaultProfile, nil +} diff --git a/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/sort_profiles.go b/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/sort_profiles.go new file mode 100644 index 0000000..15b83ec --- /dev/null +++ b/vendor/github.com/bitrise-steplib/steps-xcode-archive/utils/sort_profiles.go @@ -0,0 +1,21 @@ +package utils + +import "github.com/bitrise-io/go-xcode/profileutil" + +// ByBundleIDLength ... +type ByBundleIDLength []profileutil.ProvisioningProfileInfoModel + +// Len .. +func (s ByBundleIDLength) Len() int { + return len(s) +} + +// Swap ... +func (s ByBundleIDLength) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Less ... +func (s ByBundleIDLength) Less(i, j int) bool { + return len(s[i].BundleID) > len(s[j].BundleID) +} diff --git a/vendor/github.com/fullsailor/pkcs7/.gitignore b/vendor/github.com/fullsailor/pkcs7/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/vendor/github.com/fullsailor/pkcs7/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/fullsailor/pkcs7/.travis.yml b/vendor/github.com/fullsailor/pkcs7/.travis.yml new file mode 100644 index 0000000..bc12043 --- /dev/null +++ b/vendor/github.com/fullsailor/pkcs7/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go: + - 1.8 + - 1.9 + - "1.10" + - tip diff --git a/vendor/github.com/fullsailor/pkcs7/LICENSE b/vendor/github.com/fullsailor/pkcs7/LICENSE new file mode 100644 index 0000000..75f3209 --- /dev/null +++ b/vendor/github.com/fullsailor/pkcs7/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Andrew Smith + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/fullsailor/pkcs7/README.md b/vendor/github.com/fullsailor/pkcs7/README.md new file mode 100644 index 0000000..bfd948f --- /dev/null +++ b/vendor/github.com/fullsailor/pkcs7/README.md @@ -0,0 +1,8 @@ +# pkcs7 + +[![GoDoc](https://godoc.org/github.com/fullsailor/pkcs7?status.svg)](https://godoc.org/github.com/fullsailor/pkcs7) +[![Build Status](https://travis-ci.org/fullsailor/pkcs7.svg?branch=master)](https://travis-ci.org/fullsailor/pkcs7) + +pkcs7 implements parsing and creating signed and enveloped messages. + +- Documentation on [GoDoc](http://godoc.org/github.com/fullsailor/pkcs7) diff --git a/vendor/github.com/fullsailor/pkcs7/ber.go b/vendor/github.com/fullsailor/pkcs7/ber.go new file mode 100644 index 0000000..89e96d3 --- /dev/null +++ b/vendor/github.com/fullsailor/pkcs7/ber.go @@ -0,0 +1,248 @@ +package pkcs7 + +import ( + "bytes" + "errors" +) + +// var encodeIndent = 0 + +type asn1Object interface { + EncodeTo(writer *bytes.Buffer) error +} + +type asn1Structured struct { + tagBytes []byte + content []asn1Object +} + +func (s asn1Structured) EncodeTo(out *bytes.Buffer) error { + //fmt.Printf("%s--> tag: % X\n", strings.Repeat("| ", encodeIndent), s.tagBytes) + //encodeIndent++ + inner := new(bytes.Buffer) + for _, obj := range s.content { + err := obj.EncodeTo(inner) + if err != nil { + return err + } + } + //encodeIndent-- + out.Write(s.tagBytes) + encodeLength(out, inner.Len()) + out.Write(inner.Bytes()) + return nil +} + +type asn1Primitive struct { + tagBytes []byte + length int + content []byte +} + +func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error { + _, err := out.Write(p.tagBytes) + if err != nil { + return err + } + if err = encodeLength(out, p.length); err != nil { + return err + } + //fmt.Printf("%s--> tag: % X length: %d\n", strings.Repeat("| ", encodeIndent), p.tagBytes, p.length) + //fmt.Printf("%s--> content length: %d\n", strings.Repeat("| ", encodeIndent), len(p.content)) + out.Write(p.content) + + return nil +} + +func ber2der(ber []byte) ([]byte, error) { + if len(ber) == 0 { + return nil, errors.New("ber2der: input ber is empty") + } + //fmt.Printf("--> ber2der: Transcoding %d bytes\n", len(ber)) + out := new(bytes.Buffer) + + obj, _, err := readObject(ber, 0) + if err != nil { + return nil, err + } + obj.EncodeTo(out) + + // if offset < len(ber) { + // return nil, fmt.Errorf("ber2der: Content longer than expected. Got %d, expected %d", offset, len(ber)) + //} + + return out.Bytes(), nil +} + +// encodes lengths that are longer than 127 into string of bytes +func marshalLongLength(out *bytes.Buffer, i int) (err error) { + n := lengthLength(i) + + for ; n > 0; n-- { + err = out.WriteByte(byte(i >> uint((n-1)*8))) + if err != nil { + return + } + } + + return nil +} + +// computes the byte length of an encoded length value +func lengthLength(i int) (numBytes int) { + numBytes = 1 + for i > 255 { + numBytes++ + i >>= 8 + } + return +} + +// encodes the length in DER format +// If the length fits in 7 bits, the value is encoded directly. +// +// Otherwise, the number of bytes to encode the length is first determined. +// This number is likely to be 4 or less for a 32bit length. This number is +// added to 0x80. The length is encoded in big endian encoding follow after +// +// Examples: +// length | byte 1 | bytes n +// 0 | 0x00 | - +// 120 | 0x78 | - +// 200 | 0x81 | 0xC8 +// 500 | 0x82 | 0x01 0xF4 +// +func encodeLength(out *bytes.Buffer, length int) (err error) { + if length >= 128 { + l := lengthLength(length) + err = out.WriteByte(0x80 | byte(l)) + if err != nil { + return + } + err = marshalLongLength(out, length) + if err != nil { + return + } + } else { + err = out.WriteByte(byte(length)) + if err != nil { + return + } + } + return +} + +func readObject(ber []byte, offset int) (asn1Object, int, error) { + //fmt.Printf("\n====> Starting readObject at offset: %d\n\n", offset) + tagStart := offset + b := ber[offset] + offset++ + tag := b & 0x1F // last 5 bits + if tag == 0x1F { + tag = 0 + for ber[offset] >= 0x80 { + tag = tag*128 + ber[offset] - 0x80 + offset++ + } + tag = tag*128 + ber[offset] - 0x80 + offset++ + } + tagEnd := offset + + kind := b & 0x20 + /* + if kind == 0 { + fmt.Print("--> Primitive\n") + } else { + fmt.Print("--> Constructed\n") + } + */ + // read length + var length int + l := ber[offset] + offset++ + indefinite := false + if l > 0x80 { + numberOfBytes := (int)(l & 0x7F) + if numberOfBytes > 4 { // int is only guaranteed to be 32bit + return nil, 0, errors.New("ber2der: BER tag length too long") + } + if numberOfBytes == 4 && (int)(ber[offset]) > 0x7F { + return nil, 0, errors.New("ber2der: BER tag length is negative") + } + if 0x0 == (int)(ber[offset]) { + return nil, 0, errors.New("ber2der: BER tag length has leading zero") + } + //fmt.Printf("--> (compute length) indicator byte: %x\n", l) + //fmt.Printf("--> (compute length) length bytes: % X\n", ber[offset:offset+numberOfBytes]) + for i := 0; i < numberOfBytes; i++ { + length = length*256 + (int)(ber[offset]) + offset++ + } + } else if l == 0x80 { + indefinite = true + } else { + length = (int)(l) + } + + //fmt.Printf("--> length : %d\n", length) + contentEnd := offset + length + if contentEnd > len(ber) { + return nil, 0, errors.New("ber2der: BER tag length is more than available data") + } + //fmt.Printf("--> content start : %d\n", offset) + //fmt.Printf("--> content end : %d\n", contentEnd) + //fmt.Printf("--> content : % X\n", ber[offset:contentEnd]) + var obj asn1Object + if indefinite && kind == 0 { + return nil, 0, errors.New("ber2der: Indefinite form tag must have constructed encoding") + } + if kind == 0 { + obj = asn1Primitive{ + tagBytes: ber[tagStart:tagEnd], + length: length, + content: ber[offset:contentEnd], + } + } else { + var subObjects []asn1Object + for (offset < contentEnd) || indefinite { + var subObj asn1Object + var err error + subObj, offset, err = readObject(ber, offset) + if err != nil { + return nil, 0, err + } + subObjects = append(subObjects, subObj) + + if indefinite { + terminated, err := isIndefiniteTermination(ber, offset) + if err != nil { + return nil, 0, err + } + + if terminated { + break + } + } + } + obj = asn1Structured{ + tagBytes: ber[tagStart:tagEnd], + content: subObjects, + } + } + + // Apply indefinite form length with 0x0000 terminator. + if indefinite { + contentEnd = offset + 2 + } + + return obj, contentEnd, nil +} + +func isIndefiniteTermination(ber []byte, offset int) (bool, error) { + if len(ber) - offset < 2 { + return false, errors.New("ber2der: Invalid BER format") + } + + return bytes.Index(ber[offset:], []byte{0x0, 0x0}) == 0, nil +} diff --git a/vendor/github.com/fullsailor/pkcs7/pkcs7.go b/vendor/github.com/fullsailor/pkcs7/pkcs7.go new file mode 100644 index 0000000..0264466 --- /dev/null +++ b/vendor/github.com/fullsailor/pkcs7/pkcs7.go @@ -0,0 +1,962 @@ +// Package pkcs7 implements parsing and generation of some PKCS#7 structures. +package pkcs7 + +import ( + "bytes" + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/hmac" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" + "math/big" + "sort" + "time" + + _ "crypto/sha1" // for crypto.SHA1 +) + +// PKCS7 Represents a PKCS7 structure +type PKCS7 struct { + Content []byte + Certificates []*x509.Certificate + CRLs []pkix.CertificateList + Signers []signerInfo + raw interface{} +} + +type contentInfo struct { + ContentType asn1.ObjectIdentifier + Content asn1.RawValue `asn1:"explicit,optional,tag:0"` +} + +// ErrUnsupportedContentType is returned when a PKCS7 content is not supported. +// Currently only Data (1.2.840.113549.1.7.1), Signed Data (1.2.840.113549.1.7.2), +// and Enveloped Data are supported (1.2.840.113549.1.7.3) +var ErrUnsupportedContentType = errors.New("pkcs7: cannot parse data: unimplemented content type") + +type unsignedData []byte + +var ( + oidData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1} + oidSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2} + oidEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 3} + oidSignedAndEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 4} + oidDigestedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 5} + oidEncryptedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 6} + oidAttributeContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3} + oidAttributeMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4} + oidAttributeSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5} +) + +type signedData struct { + Version int `asn1:"default:1"` + DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"` + ContentInfo contentInfo + Certificates rawCertificates `asn1:"optional,tag:0"` + CRLs []pkix.CertificateList `asn1:"optional,tag:1"` + SignerInfos []signerInfo `asn1:"set"` +} + +type rawCertificates struct { + Raw asn1.RawContent +} + +type envelopedData struct { + Version int + RecipientInfos []recipientInfo `asn1:"set"` + EncryptedContentInfo encryptedContentInfo +} + +type recipientInfo struct { + Version int + IssuerAndSerialNumber issuerAndSerial + KeyEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedKey []byte +} + +type encryptedContentInfo struct { + ContentType asn1.ObjectIdentifier + ContentEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedContent asn1.RawValue `asn1:"tag:0,optional"` +} + +type attribute struct { + Type asn1.ObjectIdentifier + Value asn1.RawValue `asn1:"set"` +} + +type issuerAndSerial struct { + IssuerName asn1.RawValue + SerialNumber *big.Int +} + +// MessageDigestMismatchError is returned when the signer data digest does not +// match the computed digest for the contained content +type MessageDigestMismatchError struct { + ExpectedDigest []byte + ActualDigest []byte +} + +func (err *MessageDigestMismatchError) Error() string { + return fmt.Sprintf("pkcs7: Message digest mismatch\n\tExpected: %X\n\tActual : %X", err.ExpectedDigest, err.ActualDigest) +} + +type signerInfo struct { + Version int `asn1:"default:1"` + IssuerAndSerialNumber issuerAndSerial + DigestAlgorithm pkix.AlgorithmIdentifier + AuthenticatedAttributes []attribute `asn1:"optional,tag:0"` + DigestEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedDigest []byte + UnauthenticatedAttributes []attribute `asn1:"optional,tag:1"` +} + +// Parse decodes a DER encoded PKCS7 package +func Parse(data []byte) (p7 *PKCS7, err error) { + if len(data) == 0 { + return nil, errors.New("pkcs7: input data is empty") + } + var info contentInfo + der, err := ber2der(data) + if err != nil { + return nil, err + } + rest, err := asn1.Unmarshal(der, &info) + if len(rest) > 0 { + err = asn1.SyntaxError{Msg: "trailing data"} + return + } + if err != nil { + return + } + + // fmt.Printf("--> Content Type: %s", info.ContentType) + switch { + case info.ContentType.Equal(oidSignedData): + return parseSignedData(info.Content.Bytes) + case info.ContentType.Equal(oidEnvelopedData): + return parseEnvelopedData(info.Content.Bytes) + } + return nil, ErrUnsupportedContentType +} + +func parseSignedData(data []byte) (*PKCS7, error) { + var sd signedData + asn1.Unmarshal(data, &sd) + certs, err := sd.Certificates.Parse() + if err != nil { + return nil, err + } + // fmt.Printf("--> Signed Data Version %d\n", sd.Version) + + var compound asn1.RawValue + var content unsignedData + + // The Content.Bytes maybe empty on PKI responses. + if len(sd.ContentInfo.Content.Bytes) > 0 { + if _, err := asn1.Unmarshal(sd.ContentInfo.Content.Bytes, &compound); err != nil { + return nil, err + } + } + // Compound octet string + if compound.IsCompound { + if _, err = asn1.Unmarshal(compound.Bytes, &content); err != nil { + return nil, err + } + } else { + // assuming this is tag 04 + content = compound.Bytes + } + return &PKCS7{ + Content: content, + Certificates: certs, + CRLs: sd.CRLs, + Signers: sd.SignerInfos, + raw: sd}, nil +} + +func (raw rawCertificates) Parse() ([]*x509.Certificate, error) { + if len(raw.Raw) == 0 { + return nil, nil + } + + var val asn1.RawValue + if _, err := asn1.Unmarshal(raw.Raw, &val); err != nil { + return nil, err + } + + return x509.ParseCertificates(val.Bytes) +} + +func parseEnvelopedData(data []byte) (*PKCS7, error) { + var ed envelopedData + if _, err := asn1.Unmarshal(data, &ed); err != nil { + return nil, err + } + return &PKCS7{ + raw: ed, + }, nil +} + +// Verify checks the signatures of a PKCS7 object +// WARNING: Verify does not check signing time or verify certificate chains at +// this time. +func (p7 *PKCS7) Verify() (err error) { + if len(p7.Signers) == 0 { + return errors.New("pkcs7: Message has no signers") + } + for _, signer := range p7.Signers { + if err := verifySignature(p7, signer); err != nil { + return err + } + } + return nil +} + +func verifySignature(p7 *PKCS7, signer signerInfo) error { + signedData := p7.Content + hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm) + if err != nil { + return err + } + if len(signer.AuthenticatedAttributes) > 0 { + // TODO(fullsailor): First check the content type match + var digest []byte + err := unmarshalAttribute(signer.AuthenticatedAttributes, oidAttributeMessageDigest, &digest) + if err != nil { + return err + } + h := hash.New() + h.Write(p7.Content) + computed := h.Sum(nil) + if !hmac.Equal(digest, computed) { + return &MessageDigestMismatchError{ + ExpectedDigest: digest, + ActualDigest: computed, + } + } + // TODO(fullsailor): Optionally verify certificate chain + // TODO(fullsailor): Optionally verify signingTime against certificate NotAfter/NotBefore + signedData, err = marshalAttributes(signer.AuthenticatedAttributes) + if err != nil { + return err + } + } + cert := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) + if cert == nil { + return errors.New("pkcs7: No certificate for signer") + } + + algo := getSignatureAlgorithmFromAI(signer.DigestEncryptionAlgorithm) + if algo == x509.UnknownSignatureAlgorithm { + // I'm not sure what the spec here is, and the openssl sources were not + // helpful. But, this is what App Store receipts appear to do. + // The DigestEncryptionAlgorithm is just "rsaEncryption (PKCS #1)" + // But we're expecting a digest + encryption algorithm. So... we're going + // to determine an algorithm based on the DigestAlgorithm and this + // encryption algorithm. + if signer.DigestEncryptionAlgorithm.Algorithm.Equal(oidEncryptionAlgorithmRSA) { + algo = getRSASignatureAlgorithmForDigestAlgorithm(hash) + } + } + return cert.CheckSignature(algo, signedData, signer.EncryptedDigest) +} + +func marshalAttributes(attrs []attribute) ([]byte, error) { + encodedAttributes, err := asn1.Marshal(struct { + A []attribute `asn1:"set"` + }{A: attrs}) + if err != nil { + return nil, err + } + + // Remove the leading sequence octets + var raw asn1.RawValue + asn1.Unmarshal(encodedAttributes, &raw) + return raw.Bytes, nil +} + +var ( + oidDigestAlgorithmSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26} + oidEncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} +) + +func getCertFromCertsByIssuerAndSerial(certs []*x509.Certificate, ias issuerAndSerial) *x509.Certificate { + for _, cert := range certs { + if isCertMatchForIssuerAndSerial(cert, ias) { + return cert + } + } + return nil +} + +func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) { + switch { + case oid.Equal(oidDigestAlgorithmSHA1): + return crypto.SHA1, nil + case oid.Equal(oidSHA256): + return crypto.SHA256, nil + } + return crypto.Hash(0), ErrUnsupportedAlgorithm +} + +func getRSASignatureAlgorithmForDigestAlgorithm(hash crypto.Hash) x509.SignatureAlgorithm { + for _, details := range signatureAlgorithmDetails { + if details.pubKeyAlgo == x509.RSA && details.hash == hash { + return details.algo + } + } + return x509.UnknownSignatureAlgorithm +} + +// GetOnlySigner returns an x509.Certificate for the first signer of the signed +// data payload. If there are more or less than one signer, nil is returned +func (p7 *PKCS7) GetOnlySigner() *x509.Certificate { + if len(p7.Signers) != 1 { + return nil + } + signer := p7.Signers[0] + return getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) +} + +// ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed +var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, DES, DES-EDE3, AES-256-CBC and AES-128-GCM supported") + +// ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data +var ErrNotEncryptedContent = errors.New("pkcs7: content data is a decryptable data type") + +// Decrypt decrypts encrypted content info for recipient cert and private key +func (p7 *PKCS7) Decrypt(cert *x509.Certificate, pk crypto.PrivateKey) ([]byte, error) { + data, ok := p7.raw.(envelopedData) + if !ok { + return nil, ErrNotEncryptedContent + } + recipient := selectRecipientForCertificate(data.RecipientInfos, cert) + if recipient.EncryptedKey == nil { + return nil, errors.New("pkcs7: no enveloped recipient for provided certificate") + } + if priv := pk.(*rsa.PrivateKey); priv != nil { + var contentKey []byte + contentKey, err := rsa.DecryptPKCS1v15(rand.Reader, priv, recipient.EncryptedKey) + if err != nil { + return nil, err + } + return data.EncryptedContentInfo.decrypt(contentKey) + } + fmt.Printf("Unsupported Private Key: %v\n", pk) + return nil, ErrUnsupportedAlgorithm +} + +var oidEncryptionAlgorithmDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7} +var oidEncryptionAlgorithmDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7} +var oidEncryptionAlgorithmAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42} +var oidEncryptionAlgorithmAES128GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6} +var oidEncryptionAlgorithmAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2} + +func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) { + alg := eci.ContentEncryptionAlgorithm.Algorithm + if !alg.Equal(oidEncryptionAlgorithmDESCBC) && + !alg.Equal(oidEncryptionAlgorithmDESEDE3CBC) && + !alg.Equal(oidEncryptionAlgorithmAES256CBC) && + !alg.Equal(oidEncryptionAlgorithmAES128CBC) && + !alg.Equal(oidEncryptionAlgorithmAES128GCM) { + fmt.Printf("Unsupported Content Encryption Algorithm: %s\n", alg) + return nil, ErrUnsupportedAlgorithm + } + + // EncryptedContent can either be constructed of multple OCTET STRINGs + // or _be_ a tagged OCTET STRING + var cyphertext []byte + if eci.EncryptedContent.IsCompound { + // Complex case to concat all of the children OCTET STRINGs + var buf bytes.Buffer + cypherbytes := eci.EncryptedContent.Bytes + for { + var part []byte + cypherbytes, _ = asn1.Unmarshal(cypherbytes, &part) + buf.Write(part) + if cypherbytes == nil { + break + } + } + cyphertext = buf.Bytes() + } else { + // Simple case, the bytes _are_ the cyphertext + cyphertext = eci.EncryptedContent.Bytes + } + + var block cipher.Block + var err error + + switch { + case alg.Equal(oidEncryptionAlgorithmDESCBC): + block, err = des.NewCipher(key) + case alg.Equal(oidEncryptionAlgorithmDESEDE3CBC): + block, err = des.NewTripleDESCipher(key) + case alg.Equal(oidEncryptionAlgorithmAES256CBC): + fallthrough + case alg.Equal(oidEncryptionAlgorithmAES128GCM), alg.Equal(oidEncryptionAlgorithmAES128CBC): + block, err = aes.NewCipher(key) + } + + if err != nil { + return nil, err + } + + if alg.Equal(oidEncryptionAlgorithmAES128GCM) { + params := aesGCMParameters{} + paramBytes := eci.ContentEncryptionAlgorithm.Parameters.Bytes + + _, err := asn1.Unmarshal(paramBytes, ¶ms) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + if len(params.Nonce) != gcm.NonceSize() { + return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") + } + if params.ICVLen != gcm.Overhead() { + return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") + } + + plaintext, err := gcm.Open(nil, params.Nonce, cyphertext, nil) + if err != nil { + return nil, err + } + + return plaintext, nil + } + + iv := eci.ContentEncryptionAlgorithm.Parameters.Bytes + if len(iv) != block.BlockSize() { + return nil, errors.New("pkcs7: encryption algorithm parameters are malformed") + } + mode := cipher.NewCBCDecrypter(block, iv) + plaintext := make([]byte, len(cyphertext)) + mode.CryptBlocks(plaintext, cyphertext) + if plaintext, err = unpad(plaintext, mode.BlockSize()); err != nil { + return nil, err + } + return plaintext, nil +} + +func selectRecipientForCertificate(recipients []recipientInfo, cert *x509.Certificate) recipientInfo { + for _, recp := range recipients { + if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) { + return recp + } + } + return recipientInfo{} +} + +func isCertMatchForIssuerAndSerial(cert *x509.Certificate, ias issuerAndSerial) bool { + return cert.SerialNumber.Cmp(ias.SerialNumber) == 0 && bytes.Compare(cert.RawIssuer, ias.IssuerName.FullBytes) == 0 +} + +func pad(data []byte, blocklen int) ([]byte, error) { + if blocklen < 1 { + return nil, fmt.Errorf("invalid blocklen %d", blocklen) + } + padlen := blocklen - (len(data) % blocklen) + if padlen == 0 { + padlen = blocklen + } + pad := bytes.Repeat([]byte{byte(padlen)}, padlen) + return append(data, pad...), nil +} + +func unpad(data []byte, blocklen int) ([]byte, error) { + if blocklen < 1 { + return nil, fmt.Errorf("invalid blocklen %d", blocklen) + } + if len(data)%blocklen != 0 || len(data) == 0 { + return nil, fmt.Errorf("invalid data len %d", len(data)) + } + + // the last byte is the length of padding + padlen := int(data[len(data)-1]) + + // check padding integrity, all bytes should be the same + pad := data[len(data)-padlen:] + for _, padbyte := range pad { + if padbyte != byte(padlen) { + return nil, errors.New("invalid padding") + } + } + + return data[:len(data)-padlen], nil +} + +func unmarshalAttribute(attrs []attribute, attributeType asn1.ObjectIdentifier, out interface{}) error { + for _, attr := range attrs { + if attr.Type.Equal(attributeType) { + _, err := asn1.Unmarshal(attr.Value.Bytes, out) + return err + } + } + return errors.New("pkcs7: attribute type not in attributes") +} + +// UnmarshalSignedAttribute decodes a single attribute from the signer info +func (p7 *PKCS7) UnmarshalSignedAttribute(attributeType asn1.ObjectIdentifier, out interface{}) error { + sd, ok := p7.raw.(signedData) + if !ok { + return errors.New("pkcs7: payload is not signedData content") + } + if len(sd.SignerInfos) < 1 { + return errors.New("pkcs7: payload has no signers") + } + attributes := sd.SignerInfos[0].AuthenticatedAttributes + return unmarshalAttribute(attributes, attributeType, out) +} + +// SignedData is an opaque data structure for creating signed data payloads +type SignedData struct { + sd signedData + certs []*x509.Certificate + messageDigest []byte +} + +// Attribute represents a key value pair attribute. Value must be marshalable byte +// `encoding/asn1` +type Attribute struct { + Type asn1.ObjectIdentifier + Value interface{} +} + +// SignerInfoConfig are optional values to include when adding a signer +type SignerInfoConfig struct { + ExtraSignedAttributes []Attribute +} + +// NewSignedData initializes a SignedData with content +func NewSignedData(data []byte) (*SignedData, error) { + content, err := asn1.Marshal(data) + if err != nil { + return nil, err + } + ci := contentInfo{ + ContentType: oidData, + Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true}, + } + digAlg := pkix.AlgorithmIdentifier{ + Algorithm: oidDigestAlgorithmSHA1, + } + h := crypto.SHA1.New() + h.Write(data) + md := h.Sum(nil) + sd := signedData{ + ContentInfo: ci, + Version: 1, + DigestAlgorithmIdentifiers: []pkix.AlgorithmIdentifier{digAlg}, + } + return &SignedData{sd: sd, messageDigest: md}, nil +} + +type attributes struct { + types []asn1.ObjectIdentifier + values []interface{} +} + +// Add adds the attribute, maintaining insertion order +func (attrs *attributes) Add(attrType asn1.ObjectIdentifier, value interface{}) { + attrs.types = append(attrs.types, attrType) + attrs.values = append(attrs.values, value) +} + +type sortableAttribute struct { + SortKey []byte + Attribute attribute +} + +type attributeSet []sortableAttribute + +func (sa attributeSet) Len() int { + return len(sa) +} + +func (sa attributeSet) Less(i, j int) bool { + return bytes.Compare(sa[i].SortKey, sa[j].SortKey) < 0 +} + +func (sa attributeSet) Swap(i, j int) { + sa[i], sa[j] = sa[j], sa[i] +} + +func (sa attributeSet) Attributes() []attribute { + attrs := make([]attribute, len(sa)) + for i, attr := range sa { + attrs[i] = attr.Attribute + } + return attrs +} + +func (attrs *attributes) ForMarshaling() ([]attribute, error) { + sortables := make(attributeSet, len(attrs.types)) + for i := range sortables { + attrType := attrs.types[i] + attrValue := attrs.values[i] + asn1Value, err := asn1.Marshal(attrValue) + if err != nil { + return nil, err + } + attr := attribute{ + Type: attrType, + Value: asn1.RawValue{Tag: 17, IsCompound: true, Bytes: asn1Value}, // 17 == SET tag + } + encoded, err := asn1.Marshal(attr) + if err != nil { + return nil, err + } + sortables[i] = sortableAttribute{ + SortKey: encoded, + Attribute: attr, + } + } + sort.Sort(sortables) + return sortables.Attributes(), nil +} + +// AddSigner signs attributes about the content and adds certificate to payload +func (sd *SignedData) AddSigner(cert *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error { + attrs := &attributes{} + attrs.Add(oidAttributeContentType, sd.sd.ContentInfo.ContentType) + attrs.Add(oidAttributeMessageDigest, sd.messageDigest) + attrs.Add(oidAttributeSigningTime, time.Now()) + for _, attr := range config.ExtraSignedAttributes { + attrs.Add(attr.Type, attr.Value) + } + finalAttrs, err := attrs.ForMarshaling() + if err != nil { + return err + } + signature, err := signAttributes(finalAttrs, pkey, crypto.SHA1) + if err != nil { + return err + } + + ias, err := cert2issuerAndSerial(cert) + if err != nil { + return err + } + + signer := signerInfo{ + AuthenticatedAttributes: finalAttrs, + DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidDigestAlgorithmSHA1}, + DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSignatureSHA1WithRSA}, + IssuerAndSerialNumber: ias, + EncryptedDigest: signature, + Version: 1, + } + // create signature of signed attributes + sd.certs = append(sd.certs, cert) + sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer) + return nil +} + +// AddCertificate adds the certificate to the payload. Useful for parent certificates +func (sd *SignedData) AddCertificate(cert *x509.Certificate) { + sd.certs = append(sd.certs, cert) +} + +// Detach removes content from the signed data struct to make it a detached signature. +// This must be called right before Finish() +func (sd *SignedData) Detach() { + sd.sd.ContentInfo = contentInfo{ContentType: oidData} +} + +// Finish marshals the content and its signers +func (sd *SignedData) Finish() ([]byte, error) { + sd.sd.Certificates = marshalCertificates(sd.certs) + inner, err := asn1.Marshal(sd.sd) + if err != nil { + return nil, err + } + outer := contentInfo{ + ContentType: oidSignedData, + Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true}, + } + return asn1.Marshal(outer) +} + +func cert2issuerAndSerial(cert *x509.Certificate) (issuerAndSerial, error) { + var ias issuerAndSerial + // The issuer RDNSequence has to match exactly the sequence in the certificate + // We cannot use cert.Issuer.ToRDNSequence() here since it mangles the sequence + ias.IssuerName = asn1.RawValue{FullBytes: cert.RawIssuer} + ias.SerialNumber = cert.SerialNumber + + return ias, nil +} + +// signs the DER encoded form of the attributes with the private key +func signAttributes(attrs []attribute, pkey crypto.PrivateKey, hash crypto.Hash) ([]byte, error) { + attrBytes, err := marshalAttributes(attrs) + if err != nil { + return nil, err + } + h := hash.New() + h.Write(attrBytes) + hashed := h.Sum(nil) + switch priv := pkey.(type) { + case *rsa.PrivateKey: + return rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA1, hashed) + } + return nil, ErrUnsupportedAlgorithm +} + +// concats and wraps the certificates in the RawValue structure +func marshalCertificates(certs []*x509.Certificate) rawCertificates { + var buf bytes.Buffer + for _, cert := range certs { + buf.Write(cert.Raw) + } + rawCerts, _ := marshalCertificateBytes(buf.Bytes()) + return rawCerts +} + +// Even though, the tag & length are stripped out during marshalling the +// RawContent, we have to encode it into the RawContent. If its missing, +// then `asn1.Marshal()` will strip out the certificate wrapper instead. +func marshalCertificateBytes(certs []byte) (rawCertificates, error) { + var val = asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true} + b, err := asn1.Marshal(val) + if err != nil { + return rawCertificates{}, err + } + return rawCertificates{Raw: b}, nil +} + +// DegenerateCertificate creates a signed data structure containing only the +// provided certificate or certificate chain. +func DegenerateCertificate(cert []byte) ([]byte, error) { + rawCert, err := marshalCertificateBytes(cert) + if err != nil { + return nil, err + } + emptyContent := contentInfo{ContentType: oidData} + sd := signedData{ + Version: 1, + ContentInfo: emptyContent, + Certificates: rawCert, + CRLs: []pkix.CertificateList{}, + } + content, err := asn1.Marshal(sd) + if err != nil { + return nil, err + } + signedContent := contentInfo{ + ContentType: oidSignedData, + Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true}, + } + return asn1.Marshal(signedContent) +} + +const ( + EncryptionAlgorithmDESCBC = iota + EncryptionAlgorithmAES128GCM +) + +// ContentEncryptionAlgorithm determines the algorithm used to encrypt the +// plaintext message. Change the value of this variable to change which +// algorithm is used in the Encrypt() function. +var ContentEncryptionAlgorithm = EncryptionAlgorithmDESCBC + +// ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt +// content with an unsupported algorithm. +var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC and AES-128-GCM supported") + +const nonceSize = 12 + +type aesGCMParameters struct { + Nonce []byte `asn1:"tag:4"` + ICVLen int +} + +func encryptAES128GCM(content []byte) ([]byte, *encryptedContentInfo, error) { + // Create AES key and nonce + key := make([]byte, 16) + nonce := make([]byte, nonceSize) + + _, err := rand.Read(key) + if err != nil { + return nil, nil, err + } + + _, err = rand.Read(nonce) + if err != nil { + return nil, nil, err + } + + // Encrypt content + block, err := aes.NewCipher(key) + if err != nil { + return nil, nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, nil, err + } + + ciphertext := gcm.Seal(nil, nonce, content, nil) + + // Prepare ASN.1 Encrypted Content Info + paramSeq := aesGCMParameters{ + Nonce: nonce, + ICVLen: gcm.Overhead(), + } + + paramBytes, err := asn1.Marshal(paramSeq) + if err != nil { + return nil, nil, err + } + + eci := encryptedContentInfo{ + ContentType: oidData, + ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: oidEncryptionAlgorithmAES128GCM, + Parameters: asn1.RawValue{ + Tag: asn1.TagSequence, + Bytes: paramBytes, + }, + }, + EncryptedContent: marshalEncryptedContent(ciphertext), + } + + return key, &eci, nil +} + +func encryptDESCBC(content []byte) ([]byte, *encryptedContentInfo, error) { + // Create DES key & CBC IV + key := make([]byte, 8) + iv := make([]byte, des.BlockSize) + _, err := rand.Read(key) + if err != nil { + return nil, nil, err + } + _, err = rand.Read(iv) + if err != nil { + return nil, nil, err + } + + // Encrypt padded content + block, err := des.NewCipher(key) + if err != nil { + return nil, nil, err + } + mode := cipher.NewCBCEncrypter(block, iv) + plaintext, err := pad(content, mode.BlockSize()) + cyphertext := make([]byte, len(plaintext)) + mode.CryptBlocks(cyphertext, plaintext) + + // Prepare ASN.1 Encrypted Content Info + eci := encryptedContentInfo{ + ContentType: oidData, + ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: oidEncryptionAlgorithmDESCBC, + Parameters: asn1.RawValue{Tag: 4, Bytes: iv}, + }, + EncryptedContent: marshalEncryptedContent(cyphertext), + } + + return key, &eci, nil +} + +// Encrypt creates and returns an envelope data PKCS7 structure with encrypted +// recipient keys for each recipient public key. +// +// The algorithm used to perform encryption is determined by the current value +// of the global ContentEncryptionAlgorithm package variable. By default, the +// value is EncryptionAlgorithmDESCBC. To use a different algorithm, change the +// value before calling Encrypt(). For example: +// +// ContentEncryptionAlgorithm = EncryptionAlgorithmAES128GCM +// +// TODO(fullsailor): Add support for encrypting content with other algorithms +func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) { + var eci *encryptedContentInfo + var key []byte + var err error + + // Apply chosen symmetric encryption method + switch ContentEncryptionAlgorithm { + case EncryptionAlgorithmDESCBC: + key, eci, err = encryptDESCBC(content) + + case EncryptionAlgorithmAES128GCM: + key, eci, err = encryptAES128GCM(content) + + default: + return nil, ErrUnsupportedEncryptionAlgorithm + } + + if err != nil { + return nil, err + } + + // Prepare each recipient's encrypted cipher key + recipientInfos := make([]recipientInfo, len(recipients)) + for i, recipient := range recipients { + encrypted, err := encryptKey(key, recipient) + if err != nil { + return nil, err + } + ias, err := cert2issuerAndSerial(recipient) + if err != nil { + return nil, err + } + info := recipientInfo{ + Version: 0, + IssuerAndSerialNumber: ias, + KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: oidEncryptionAlgorithmRSA, + }, + EncryptedKey: encrypted, + } + recipientInfos[i] = info + } + + // Prepare envelope content + envelope := envelopedData{ + EncryptedContentInfo: *eci, + Version: 0, + RecipientInfos: recipientInfos, + } + innerContent, err := asn1.Marshal(envelope) + if err != nil { + return nil, err + } + + // Prepare outer payload structure + wrapper := contentInfo{ + ContentType: oidEnvelopedData, + Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent}, + } + + return asn1.Marshal(wrapper) +} + +func marshalEncryptedContent(content []byte) asn1.RawValue { + asn1Content, _ := asn1.Marshal(content) + return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true} +} + +func encryptKey(key []byte, recipient *x509.Certificate) ([]byte, error) { + if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil { + return rsa.EncryptPKCS1v15(rand.Reader, pub, key) + } + return nil, ErrUnsupportedAlgorithm +} diff --git a/vendor/github.com/fullsailor/pkcs7/x509.go b/vendor/github.com/fullsailor/pkcs7/x509.go new file mode 100644 index 0000000..195fd0e --- /dev/null +++ b/vendor/github.com/fullsailor/pkcs7/x509.go @@ -0,0 +1,133 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the go/golang LICENSE file. + +package pkcs7 + +// These are private constants and functions from the crypto/x509 package that +// are useful when dealing with signatures verified by x509 certificates + +import ( + "bytes" + "crypto" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" +) + +var ( + oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} + oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} + oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} + oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} + oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} + oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} + oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10} + oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} + oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2} + oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} + oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} + oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} + oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} + + oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} + oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2} + oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3} + + oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8} + + // oidISOSignatureSHA1WithRSA means the same as oidSignatureSHA1WithRSA + // but it's specified by ISO. Microsoft's makecert.exe has been known + // to produce certificates with this OID. + oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29} +) + +var signatureAlgorithmDetails = []struct { + algo x509.SignatureAlgorithm + name string + oid asn1.ObjectIdentifier + pubKeyAlgo x509.PublicKeyAlgorithm + hash crypto.Hash +}{ + {x509.MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */}, + {x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, x509.RSA, crypto.MD5}, + {x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, + {x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, + {x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256}, + {x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384}, + {x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512}, + {x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256}, + {x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384}, + {x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512}, + {x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1}, + {x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256}, + {x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1}, + {x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256}, + {x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384}, + {x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512}, +} + +// pssParameters reflects the parameters in an AlgorithmIdentifier that +// specifies RSA PSS. See https://tools.ietf.org/html/rfc3447#appendix-A.2.3 +type pssParameters struct { + // The following three fields are not marked as + // optional because the default values specify SHA-1, + // which is no longer suitable for use in signatures. + Hash pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"` + MGF pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"` + SaltLength int `asn1:"explicit,tag:2"` + TrailerField int `asn1:"optional,explicit,tag:3,default:1"` +} + +// asn1.NullBytes is not available prior to Go 1.9 +var nullBytes = []byte{5, 0} + +func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm { + if !ai.Algorithm.Equal(oidSignatureRSAPSS) { + for _, details := range signatureAlgorithmDetails { + if ai.Algorithm.Equal(details.oid) { + return details.algo + } + } + return x509.UnknownSignatureAlgorithm + } + + // RSA PSS is special because it encodes important parameters + // in the Parameters. + + var params pssParameters + if _, err := asn1.Unmarshal(ai.Parameters.FullBytes, ¶ms); err != nil { + return x509.UnknownSignatureAlgorithm + } + + var mgf1HashFunc pkix.AlgorithmIdentifier + if _, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil { + return x509.UnknownSignatureAlgorithm + } + + // PSS is greatly overburdened with options. This code forces + // them into three buckets by requiring that the MGF1 hash + // function always match the message hash function (as + // recommended in + // https://tools.ietf.org/html/rfc3447#section-8.1), that the + // salt length matches the hash length, and that the trailer + // field has the default value. + if !bytes.Equal(params.Hash.Parameters.FullBytes, nullBytes) || + !params.MGF.Algorithm.Equal(oidMGF1) || + !mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) || + !bytes.Equal(mgf1HashFunc.Parameters.FullBytes, nullBytes) || + params.TrailerField != 1 { + return x509.UnknownSignatureAlgorithm + } + + switch { + case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32: + return x509.SHA256WithRSAPSS + case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48: + return x509.SHA384WithRSAPSS + case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64: + return x509.SHA512WithRSAPSS + } + + return x509.UnknownSignatureAlgorithm +} diff --git a/vendor/github.com/pkg/errors/.gitignore b/vendor/github.com/pkg/errors/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/vendor/github.com/pkg/errors/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/pkg/errors/.travis.yml b/vendor/github.com/pkg/errors/.travis.yml new file mode 100644 index 0000000..d4b9266 --- /dev/null +++ b/vendor/github.com/pkg/errors/.travis.yml @@ -0,0 +1,15 @@ +language: go +go_import_path: github.com/pkg/errors +go: + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x + - 1.11.x + - tip + +script: + - go test -v ./... diff --git a/vendor/github.com/pkg/errors/LICENSE b/vendor/github.com/pkg/errors/LICENSE new file mode 100644 index 0000000..835ba3e --- /dev/null +++ b/vendor/github.com/pkg/errors/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, Dave Cheney +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/pkg/errors/README.md b/vendor/github.com/pkg/errors/README.md new file mode 100644 index 0000000..6483ba2 --- /dev/null +++ b/vendor/github.com/pkg/errors/README.md @@ -0,0 +1,52 @@ +# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) + +Package errors provides simple error handling primitives. + +`go get github.com/pkg/errors` + +The traditional error handling idiom in Go is roughly akin to +```go +if err != nil { + return err +} +``` +which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. + +## Adding context to an error + +The errors.Wrap function returns a new error that adds context to the original error. For example +```go +_, err := ioutil.ReadAll(r) +if err != nil { + return errors.Wrap(err, "read failed") +} +``` +## Retrieving the cause of an error + +Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. +```go +type causer interface { + Cause() error +} +``` +`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: +```go +switch err := errors.Cause(err).(type) { +case *MyError: + // handle specifically +default: + // unknown error +} +``` + +[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). + +## Contributing + +We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. + +Before proposing a change, please discuss your change by raising an issue. + +## License + +BSD-2-Clause diff --git a/vendor/github.com/pkg/errors/appveyor.yml b/vendor/github.com/pkg/errors/appveyor.yml new file mode 100644 index 0000000..a932ead --- /dev/null +++ b/vendor/github.com/pkg/errors/appveyor.yml @@ -0,0 +1,32 @@ +version: build-{build}.{branch} + +clone_folder: C:\gopath\src\github.com\pkg\errors +shallow_clone: true # for startup speed + +environment: + GOPATH: C:\gopath + +platform: + - x64 + +# http://www.appveyor.com/docs/installed-software +install: + # some helpful output for debugging builds + - go version + - go env + # pre-installed MinGW at C:\MinGW is 32bit only + # but MSYS2 at C:\msys64 has mingw64 + - set PATH=C:\msys64\mingw64\bin;%PATH% + - gcc --version + - g++ --version + +build_script: + - go install -v ./... + +test_script: + - set PATH=C:\gopath\bin;%PATH% + - go test -v ./... + +#artifacts: +# - path: '%GOPATH%\bin\*.exe' +deploy: off diff --git a/vendor/github.com/pkg/errors/errors.go b/vendor/github.com/pkg/errors/errors.go new file mode 100644 index 0000000..7421f32 --- /dev/null +++ b/vendor/github.com/pkg/errors/errors.go @@ -0,0 +1,282 @@ +// Package errors provides simple error handling primitives. +// +// The traditional error handling idiom in Go is roughly akin to +// +// if err != nil { +// return err +// } +// +// which when applied recursively up the call stack results in error reports +// without context or debugging information. The errors package allows +// programmers to add context to the failure path in their code in a way +// that does not destroy the original value of the error. +// +// Adding context to an error +// +// The errors.Wrap function returns a new error that adds context to the +// original error by recording a stack trace at the point Wrap is called, +// together with the supplied message. For example +// +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Wrap(err, "read failed") +// } +// +// If additional control is required, the errors.WithStack and +// errors.WithMessage functions destructure errors.Wrap into its component +// operations: annotating an error with a stack trace and with a message, +// respectively. +// +// Retrieving the cause of an error +// +// Using errors.Wrap constructs a stack of errors, adding context to the +// preceding error. Depending on the nature of the error it may be necessary +// to reverse the operation of errors.Wrap to retrieve the original error +// for inspection. Any error value which implements this interface +// +// type causer interface { +// Cause() error +// } +// +// can be inspected by errors.Cause. errors.Cause will recursively retrieve +// the topmost error that does not implement causer, which is assumed to be +// the original cause. For example: +// +// switch err := errors.Cause(err).(type) { +// case *MyError: +// // handle specifically +// default: +// // unknown error +// } +// +// Although the causer interface is not exported by this package, it is +// considered a part of its stable public interface. +// +// Formatted printing of errors +// +// All error values returned from this package implement fmt.Formatter and can +// be formatted by the fmt package. The following verbs are supported: +// +// %s print the error. If the error has a Cause it will be +// printed recursively. +// %v see %s +// %+v extended format. Each Frame of the error's StackTrace will +// be printed in detail. +// +// Retrieving the stack trace of an error or wrapper +// +// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are +// invoked. This information can be retrieved with the following interface: +// +// type stackTracer interface { +// StackTrace() errors.StackTrace +// } +// +// The returned errors.StackTrace type is defined as +// +// type StackTrace []Frame +// +// The Frame type represents a call site in the stack trace. Frame supports +// the fmt.Formatter interface that can be used for printing information about +// the stack trace of this error. For example: +// +// if err, ok := err.(stackTracer); ok { +// for _, f := range err.StackTrace() { +// fmt.Printf("%+s:%d", f) +// } +// } +// +// Although the stackTracer interface is not exported by this package, it is +// considered a part of its stable public interface. +// +// See the documentation for Frame.Format for more details. +package errors + +import ( + "fmt" + "io" +) + +// New returns an error with the supplied message. +// New also records the stack trace at the point it was called. +func New(message string) error { + return &fundamental{ + msg: message, + stack: callers(), + } +} + +// Errorf formats according to a format specifier and returns the string +// as a value that satisfies error. +// Errorf also records the stack trace at the point it was called. +func Errorf(format string, args ...interface{}) error { + return &fundamental{ + msg: fmt.Sprintf(format, args...), + stack: callers(), + } +} + +// fundamental is an error that has a message and a stack, but no caller. +type fundamental struct { + msg string + *stack +} + +func (f *fundamental) Error() string { return f.msg } + +func (f *fundamental) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + io.WriteString(s, f.msg) + f.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, f.msg) + case 'q': + fmt.Fprintf(s, "%q", f.msg) + } +} + +// WithStack annotates err with a stack trace at the point WithStack was called. +// If err is nil, WithStack returns nil. +func WithStack(err error) error { + if err == nil { + return nil + } + return &withStack{ + err, + callers(), + } +} + +type withStack struct { + error + *stack +} + +func (w *withStack) Cause() error { return w.error } + +func (w *withStack) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v", w.Cause()) + w.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, w.Error()) + case 'q': + fmt.Fprintf(s, "%q", w.Error()) + } +} + +// Wrap returns an error annotating err with a stack trace +// at the point Wrap is called, and the supplied message. +// If err is nil, Wrap returns nil. +func Wrap(err error, message string) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: message, + } + return &withStack{ + err, + callers(), + } +} + +// Wrapf returns an error annotating err with a stack trace +// at the point Wrapf is called, and the format specifier. +// If err is nil, Wrapf returns nil. +func Wrapf(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } + return &withStack{ + err, + callers(), + } +} + +// WithMessage annotates err with a new message. +// If err is nil, WithMessage returns nil. +func WithMessage(err error, message string) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: message, + } +} + +// WithMessagef annotates err with the format specifier. +// If err is nil, WithMessagef returns nil. +func WithMessagef(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } +} + +type withMessage struct { + cause error + msg string +} + +func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } +func (w *withMessage) Cause() error { return w.cause } + +func (w *withMessage) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", w.Cause()) + io.WriteString(s, w.msg) + return + } + fallthrough + case 's', 'q': + io.WriteString(s, w.Error()) + } +} + +// Cause returns the underlying cause of the error, if possible. +// An error value has a cause if it implements the following +// interface: +// +// type causer interface { +// Cause() error +// } +// +// If the error does not implement Cause, the original error will +// be returned. If the error is nil, nil will be returned without further +// investigation. +func Cause(err error) error { + type causer interface { + Cause() error + } + + for err != nil { + cause, ok := err.(causer) + if !ok { + break + } + err = cause.Cause() + } + return err +} diff --git a/vendor/github.com/pkg/errors/stack.go b/vendor/github.com/pkg/errors/stack.go new file mode 100644 index 0000000..2874a04 --- /dev/null +++ b/vendor/github.com/pkg/errors/stack.go @@ -0,0 +1,147 @@ +package errors + +import ( + "fmt" + "io" + "path" + "runtime" + "strings" +) + +// Frame represents a program counter inside a stack frame. +type Frame uintptr + +// pc returns the program counter for this frame; +// multiple frames may have the same PC value. +func (f Frame) pc() uintptr { return uintptr(f) - 1 } + +// file returns the full path to the file that contains the +// function for this Frame's pc. +func (f Frame) file() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + file, _ := fn.FileLine(f.pc()) + return file +} + +// line returns the line number of source code of the +// function for this Frame's pc. +func (f Frame) line() int { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return 0 + } + _, line := fn.FileLine(f.pc()) + return line +} + +// Format formats the frame according to the fmt.Formatter interface. +// +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+s function name and path of source file relative to the compile time +// GOPATH separated by \n\t (\n\t) +// %+v equivalent to %+s:%d +func (f Frame) Format(s fmt.State, verb rune) { + switch verb { + case 's': + switch { + case s.Flag('+'): + pc := f.pc() + fn := runtime.FuncForPC(pc) + if fn == nil { + io.WriteString(s, "unknown") + } else { + file, _ := fn.FileLine(pc) + fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) + } + default: + io.WriteString(s, path.Base(f.file())) + } + case 'd': + fmt.Fprintf(s, "%d", f.line()) + case 'n': + name := runtime.FuncForPC(f.pc()).Name() + io.WriteString(s, funcname(name)) + case 'v': + f.Format(s, 's') + io.WriteString(s, ":") + f.Format(s, 'd') + } +} + +// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). +type StackTrace []Frame + +// Format formats the stack of Frames according to the fmt.Formatter interface. +// +// %s lists source files for each Frame in the stack +// %v lists the source file and line number for each Frame in the stack +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+v Prints filename, function, and line number for each Frame in the stack. +func (st StackTrace) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case s.Flag('+'): + for _, f := range st { + fmt.Fprintf(s, "\n%+v", f) + } + case s.Flag('#'): + fmt.Fprintf(s, "%#v", []Frame(st)) + default: + fmt.Fprintf(s, "%v", []Frame(st)) + } + case 's': + fmt.Fprintf(s, "%s", []Frame(st)) + } +} + +// stack represents a stack of program counters. +type stack []uintptr + +func (s *stack) Format(st fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case st.Flag('+'): + for _, pc := range *s { + f := Frame(pc) + fmt.Fprintf(st, "\n%+v", f) + } + } + } +} + +func (s *stack) StackTrace() StackTrace { + f := make([]Frame, len(*s)) + for i := 0; i < len(f); i++ { + f[i] = Frame((*s)[i]) + } + return f +} + +func callers() *stack { + const depth = 32 + var pcs [depth]uintptr + n := runtime.Callers(3, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// funcname removes the path prefix component of a function's name reported by func.Name(). +func funcname(name string) string { + i := strings.LastIndex(name, "/") + name = name[i+1:] + i = strings.Index(name, ".") + return name[i+1:] +} diff --git a/vendor/golang.org/x/text/AUTHORS b/vendor/golang.org/x/text/AUTHORS new file mode 100644 index 0000000..15167cd --- /dev/null +++ b/vendor/golang.org/x/text/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/text/CONTRIBUTORS b/vendor/golang.org/x/text/CONTRIBUTORS new file mode 100644 index 0000000..1c4577e --- /dev/null +++ b/vendor/golang.org/x/text/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/text/LICENSE b/vendor/golang.org/x/text/LICENSE new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/vendor/golang.org/x/text/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/text/PATENTS b/vendor/golang.org/x/text/PATENTS new file mode 100644 index 0000000..7330990 --- /dev/null +++ b/vendor/golang.org/x/text/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/text/internal/gen/code.go b/vendor/golang.org/x/text/internal/gen/code.go new file mode 100644 index 0000000..75435c9 --- /dev/null +++ b/vendor/golang.org/x/text/internal/gen/code.go @@ -0,0 +1,375 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gen + +import ( + "bytes" + "encoding/gob" + "fmt" + "hash" + "hash/fnv" + "io" + "log" + "os" + "reflect" + "strings" + "unicode" + "unicode/utf8" +) + +// This file contains utilities for generating code. + +// TODO: other write methods like: +// - slices, maps, types, etc. + +// CodeWriter is a utility for writing structured code. It computes the content +// hash and size of written content. It ensures there are newlines between +// written code blocks. +type CodeWriter struct { + buf bytes.Buffer + Size int + Hash hash.Hash32 // content hash + gob *gob.Encoder + // For comments we skip the usual one-line separator if they are followed by + // a code block. + skipSep bool +} + +func (w *CodeWriter) Write(p []byte) (n int, err error) { + return w.buf.Write(p) +} + +// NewCodeWriter returns a new CodeWriter. +func NewCodeWriter() *CodeWriter { + h := fnv.New32() + return &CodeWriter{Hash: h, gob: gob.NewEncoder(h)} +} + +// WriteGoFile appends the buffer with the total size of all created structures +// and writes it as a Go file to the given file with the given package name. +func (w *CodeWriter) WriteGoFile(filename, pkg string) { + f, err := os.Create(filename) + if err != nil { + log.Fatalf("Could not create file %s: %v", filename, err) + } + defer f.Close() + if _, err = w.WriteGo(f, pkg, ""); err != nil { + log.Fatalf("Error writing file %s: %v", filename, err) + } +} + +// WriteVersionedGoFile appends the buffer with the total size of all created +// structures and writes it as a Go file to the given file with the given +// package name and build tags for the current Unicode version, +func (w *CodeWriter) WriteVersionedGoFile(filename, pkg string) { + tags := buildTags() + if tags != "" { + pattern := fileToPattern(filename) + updateBuildTags(pattern) + filename = fmt.Sprintf(pattern, UnicodeVersion()) + } + f, err := os.Create(filename) + if err != nil { + log.Fatalf("Could not create file %s: %v", filename, err) + } + defer f.Close() + if _, err = w.WriteGo(f, pkg, tags); err != nil { + log.Fatalf("Error writing file %s: %v", filename, err) + } +} + +// WriteGo appends the buffer with the total size of all created structures and +// writes it as a Go file to the given writer with the given package name. +func (w *CodeWriter) WriteGo(out io.Writer, pkg, tags string) (n int, err error) { + sz := w.Size + if sz > 0 { + w.WriteComment("Total table size %d bytes (%dKiB); checksum: %X\n", sz, sz/1024, w.Hash.Sum32()) + } + defer w.buf.Reset() + return WriteGo(out, pkg, tags, w.buf.Bytes()) +} + +func (w *CodeWriter) printf(f string, x ...interface{}) { + fmt.Fprintf(w, f, x...) +} + +func (w *CodeWriter) insertSep() { + if w.skipSep { + w.skipSep = false + return + } + // Use at least two newlines to ensure a blank space between the previous + // block. WriteGoFile will remove extraneous newlines. + w.printf("\n\n") +} + +// WriteComment writes a comment block. All line starts are prefixed with "//". +// Initial empty lines are gobbled. The indentation for the first line is +// stripped from consecutive lines. +func (w *CodeWriter) WriteComment(comment string, args ...interface{}) { + s := fmt.Sprintf(comment, args...) + s = strings.Trim(s, "\n") + + // Use at least two newlines to ensure a blank space between the previous + // block. WriteGoFile will remove extraneous newlines. + w.printf("\n\n// ") + w.skipSep = true + + // strip first indent level. + sep := "\n" + for ; len(s) > 0 && (s[0] == '\t' || s[0] == ' '); s = s[1:] { + sep += s[:1] + } + + strings.NewReplacer(sep, "\n// ", "\n", "\n// ").WriteString(w, s) + + w.printf("\n") +} + +func (w *CodeWriter) writeSizeInfo(size int) { + w.printf("// Size: %d bytes\n", size) +} + +// WriteConst writes a constant of the given name and value. +func (w *CodeWriter) WriteConst(name string, x interface{}) { + w.insertSep() + v := reflect.ValueOf(x) + + switch v.Type().Kind() { + case reflect.String: + w.printf("const %s %s = ", name, typeName(x)) + w.WriteString(v.String()) + w.printf("\n") + default: + w.printf("const %s = %#v\n", name, x) + } +} + +// WriteVar writes a variable of the given name and value. +func (w *CodeWriter) WriteVar(name string, x interface{}) { + w.insertSep() + v := reflect.ValueOf(x) + oldSize := w.Size + sz := int(v.Type().Size()) + w.Size += sz + + switch v.Type().Kind() { + case reflect.String: + w.printf("var %s %s = ", name, typeName(x)) + w.WriteString(v.String()) + case reflect.Struct: + w.gob.Encode(x) + fallthrough + case reflect.Slice, reflect.Array: + w.printf("var %s = ", name) + w.writeValue(v) + w.writeSizeInfo(w.Size - oldSize) + default: + w.printf("var %s %s = ", name, typeName(x)) + w.gob.Encode(x) + w.writeValue(v) + w.writeSizeInfo(w.Size - oldSize) + } + w.printf("\n") +} + +func (w *CodeWriter) writeValue(v reflect.Value) { + x := v.Interface() + switch v.Kind() { + case reflect.String: + w.WriteString(v.String()) + case reflect.Array: + // Don't double count: callers of WriteArray count on the size being + // added, so we need to discount it here. + w.Size -= int(v.Type().Size()) + w.writeSlice(x, true) + case reflect.Slice: + w.writeSlice(x, false) + case reflect.Struct: + w.printf("%s{\n", typeName(v.Interface())) + t := v.Type() + for i := 0; i < v.NumField(); i++ { + w.printf("%s: ", t.Field(i).Name) + w.writeValue(v.Field(i)) + w.printf(",\n") + } + w.printf("}") + default: + w.printf("%#v", x) + } +} + +// WriteString writes a string literal. +func (w *CodeWriter) WriteString(s string) { + io.WriteString(w.Hash, s) // content hash + w.Size += len(s) + + const maxInline = 40 + if len(s) <= maxInline { + w.printf("%q", s) + return + } + + // We will render the string as a multi-line string. + const maxWidth = 80 - 4 - len(`"`) - len(`" +`) + + // When starting on its own line, go fmt indents line 2+ an extra level. + n, max := maxWidth, maxWidth-4 + + // As per https://golang.org/issue/18078, the compiler has trouble + // compiling the concatenation of many strings, s0 + s1 + s2 + ... + sN, + // for large N. We insert redundant, explicit parentheses to work around + // that, lowering the N at any given step: (s0 + s1 + ... + s63) + (s64 + + // ... + s127) + etc + (etc + ... + sN). + explicitParens, extraComment := len(s) > 128*1024, "" + if explicitParens { + w.printf(`(`) + extraComment = "; the redundant, explicit parens are for https://golang.org/issue/18078" + } + + // Print "" +\n, if a string does not start on its own line. + b := w.buf.Bytes() + if p := len(bytes.TrimRight(b, " \t")); p > 0 && b[p-1] != '\n' { + w.printf("\"\" + // Size: %d bytes%s\n", len(s), extraComment) + n, max = maxWidth, maxWidth + } + + w.printf(`"`) + + for sz, p, nLines := 0, 0, 0; p < len(s); { + var r rune + r, sz = utf8.DecodeRuneInString(s[p:]) + out := s[p : p+sz] + chars := 1 + if !unicode.IsPrint(r) || r == utf8.RuneError || r == '"' { + switch sz { + case 1: + out = fmt.Sprintf("\\x%02x", s[p]) + case 2, 3: + out = fmt.Sprintf("\\u%04x", r) + case 4: + out = fmt.Sprintf("\\U%08x", r) + } + chars = len(out) + } else if r == '\\' { + out = "\\" + string(r) + chars = 2 + } + if n -= chars; n < 0 { + nLines++ + if explicitParens && nLines&63 == 63 { + w.printf("\") + (\"") + } + w.printf("\" +\n\"") + n = max - len(out) + } + w.printf("%s", out) + p += sz + } + w.printf(`"`) + if explicitParens { + w.printf(`)`) + } +} + +// WriteSlice writes a slice value. +func (w *CodeWriter) WriteSlice(x interface{}) { + w.writeSlice(x, false) +} + +// WriteArray writes an array value. +func (w *CodeWriter) WriteArray(x interface{}) { + w.writeSlice(x, true) +} + +func (w *CodeWriter) writeSlice(x interface{}, isArray bool) { + v := reflect.ValueOf(x) + w.gob.Encode(v.Len()) + w.Size += v.Len() * int(v.Type().Elem().Size()) + name := typeName(x) + if isArray { + name = fmt.Sprintf("[%d]%s", v.Len(), name[strings.Index(name, "]")+1:]) + } + if isArray { + w.printf("%s{\n", name) + } else { + w.printf("%s{ // %d elements\n", name, v.Len()) + } + + switch kind := v.Type().Elem().Kind(); kind { + case reflect.String: + for _, s := range x.([]string) { + w.WriteString(s) + w.printf(",\n") + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + // nLine and nBlock are the number of elements per line and block. + nLine, nBlock, format := 8, 64, "%d," + switch kind { + case reflect.Uint8: + format = "%#02x," + case reflect.Uint16: + format = "%#04x," + case reflect.Uint32: + nLine, nBlock, format = 4, 32, "%#08x," + case reflect.Uint, reflect.Uint64: + nLine, nBlock, format = 4, 32, "%#016x," + case reflect.Int8: + nLine = 16 + } + n := nLine + for i := 0; i < v.Len(); i++ { + if i%nBlock == 0 && v.Len() > nBlock { + w.printf("// Entry %X - %X\n", i, i+nBlock-1) + } + x := v.Index(i).Interface() + w.gob.Encode(x) + w.printf(format, x) + if n--; n == 0 { + n = nLine + w.printf("\n") + } + } + w.printf("\n") + case reflect.Struct: + zero := reflect.Zero(v.Type().Elem()).Interface() + for i := 0; i < v.Len(); i++ { + x := v.Index(i).Interface() + w.gob.EncodeValue(v) + if !reflect.DeepEqual(zero, x) { + line := fmt.Sprintf("%#v,\n", x) + line = line[strings.IndexByte(line, '{'):] + w.printf("%d: ", i) + w.printf(line) + } + } + case reflect.Array: + for i := 0; i < v.Len(); i++ { + w.printf("%d: %#v,\n", i, v.Index(i).Interface()) + } + default: + panic("gen: slice elem type not supported") + } + w.printf("}") +} + +// WriteType writes a definition of the type of the given value and returns the +// type name. +func (w *CodeWriter) WriteType(x interface{}) string { + t := reflect.TypeOf(x) + w.printf("type %s struct {\n", t.Name()) + for i := 0; i < t.NumField(); i++ { + w.printf("\t%s %s\n", t.Field(i).Name, t.Field(i).Type) + } + w.printf("}\n") + return t.Name() +} + +// typeName returns the name of the go type of x. +func typeName(x interface{}) string { + t := reflect.ValueOf(x).Type() + return strings.Replace(fmt.Sprint(t), "main.", "", 1) +} diff --git a/vendor/golang.org/x/text/internal/gen/gen.go b/vendor/golang.org/x/text/internal/gen/gen.go new file mode 100644 index 0000000..c3bbf91 --- /dev/null +++ b/vendor/golang.org/x/text/internal/gen/gen.go @@ -0,0 +1,348 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gen contains common code for the various code generation tools in the +// text repository. Its usage ensures consistency between tools. +// +// This package defines command line flags that are common to most generation +// tools. The flags allow for specifying specific Unicode and CLDR versions +// in the public Unicode data repository (https://www.unicode.org/Public). +// +// A local Unicode data mirror can be set through the flag -local or the +// environment variable UNICODE_DIR. The former takes precedence. The local +// directory should follow the same structure as the public repository. +// +// IANA data can also optionally be mirrored by putting it in the iana directory +// rooted at the top of the local mirror. Beware, though, that IANA data is not +// versioned. So it is up to the developer to use the right version. +package gen // import "golang.org/x/text/internal/gen" + +import ( + "bytes" + "flag" + "fmt" + "go/build" + "go/format" + "io" + "io/ioutil" + "log" + "net/http" + "os" + "path" + "path/filepath" + "regexp" + "strings" + "sync" + "unicode" + + "golang.org/x/text/unicode/cldr" +) + +var ( + url = flag.String("url", + "https://www.unicode.org/Public", + "URL of Unicode database directory") + iana = flag.String("iana", + "http://www.iana.org", + "URL of the IANA repository") + unicodeVersion = flag.String("unicode", + getEnv("UNICODE_VERSION", unicode.Version), + "unicode version to use") + cldrVersion = flag.String("cldr", + getEnv("CLDR_VERSION", cldr.Version), + "cldr version to use") +) + +func getEnv(name, def string) string { + if v := os.Getenv(name); v != "" { + return v + } + return def +} + +// Init performs common initialization for a gen command. It parses the flags +// and sets up the standard logging parameters. +func Init() { + log.SetPrefix("") + log.SetFlags(log.Lshortfile) + flag.Parse() +} + +const header = `// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. + +` + +// UnicodeVersion reports the requested Unicode version. +func UnicodeVersion() string { + return *unicodeVersion +} + +// CLDRVersion reports the requested CLDR version. +func CLDRVersion() string { + return *cldrVersion +} + +var tags = []struct{ version, buildTags string }{ + {"9.0.0", "!go1.10"}, + {"10.0.0", "go1.10,!go1.13"}, + {"11.0.0", "go1.13,!go1.14"}, + {"12.0.0", "go1.14"}, +} + +// buildTags reports the build tags used for the current Unicode version. +func buildTags() string { + v := UnicodeVersion() + for _, e := range tags { + if e.version == v { + return e.buildTags + } + } + log.Fatalf("Unknown build tags for Unicode version %q.", v) + return "" +} + +// IsLocal reports whether data files are available locally. +func IsLocal() bool { + dir, err := localReadmeFile() + if err != nil { + return false + } + if _, err = os.Stat(dir); err != nil { + return false + } + return true +} + +// OpenUCDFile opens the requested UCD file. The file is specified relative to +// the public Unicode root directory. It will call log.Fatal if there are any +// errors. +func OpenUCDFile(file string) io.ReadCloser { + return openUnicode(path.Join(*unicodeVersion, "ucd", file)) +} + +// OpenCLDRCoreZip opens the CLDR core zip file. It will call log.Fatal if there +// are any errors. +func OpenCLDRCoreZip() io.ReadCloser { + return OpenUnicodeFile("cldr", *cldrVersion, "core.zip") +} + +// OpenUnicodeFile opens the requested file of the requested category from the +// root of the Unicode data archive. The file is specified relative to the +// public Unicode root directory. If version is "", it will use the default +// Unicode version. It will call log.Fatal if there are any errors. +func OpenUnicodeFile(category, version, file string) io.ReadCloser { + if version == "" { + version = UnicodeVersion() + } + return openUnicode(path.Join(category, version, file)) +} + +// OpenIANAFile opens the requested IANA file. The file is specified relative +// to the IANA root, which is typically either http://www.iana.org or the +// iana directory in the local mirror. It will call log.Fatal if there are any +// errors. +func OpenIANAFile(path string) io.ReadCloser { + return Open(*iana, "iana", path) +} + +var ( + dirMutex sync.Mutex + localDir string +) + +const permissions = 0755 + +func localReadmeFile() (string, error) { + p, err := build.Import("golang.org/x/text", "", build.FindOnly) + if err != nil { + return "", fmt.Errorf("Could not locate package: %v", err) + } + return filepath.Join(p.Dir, "DATA", "README"), nil +} + +func getLocalDir() string { + dirMutex.Lock() + defer dirMutex.Unlock() + + readme, err := localReadmeFile() + if err != nil { + log.Fatal(err) + } + dir := filepath.Dir(readme) + if _, err := os.Stat(readme); err != nil { + if err := os.MkdirAll(dir, permissions); err != nil { + log.Fatalf("Could not create directory: %v", err) + } + ioutil.WriteFile(readme, []byte(readmeTxt), permissions) + } + return dir +} + +const readmeTxt = `Generated by golang.org/x/text/internal/gen. DO NOT EDIT. + +This directory contains downloaded files used to generate the various tables +in the golang.org/x/text subrepo. + +Note that the language subtag repo (iana/assignments/language-subtag-registry) +and all other times in the iana subdirectory are not versioned and will need +to be periodically manually updated. The easiest way to do this is to remove +the entire iana directory. This is mostly of concern when updating the language +package. +` + +// Open opens subdir/path if a local directory is specified and the file exists, +// where subdir is a directory relative to the local root, or fetches it from +// urlRoot/path otherwise. It will call log.Fatal if there are any errors. +func Open(urlRoot, subdir, path string) io.ReadCloser { + file := filepath.Join(getLocalDir(), subdir, filepath.FromSlash(path)) + return open(file, urlRoot, path) +} + +func openUnicode(path string) io.ReadCloser { + file := filepath.Join(getLocalDir(), filepath.FromSlash(path)) + return open(file, *url, path) +} + +// TODO: automatically periodically update non-versioned files. + +func open(file, urlRoot, path string) io.ReadCloser { + if f, err := os.Open(file); err == nil { + return f + } + r := get(urlRoot, path) + defer r.Close() + b, err := ioutil.ReadAll(r) + if err != nil { + log.Fatalf("Could not download file: %v", err) + } + os.MkdirAll(filepath.Dir(file), permissions) + if err := ioutil.WriteFile(file, b, permissions); err != nil { + log.Fatalf("Could not create file: %v", err) + } + return ioutil.NopCloser(bytes.NewReader(b)) +} + +func get(root, path string) io.ReadCloser { + url := root + "/" + path + fmt.Printf("Fetching %s...", url) + defer fmt.Println(" done.") + resp, err := http.Get(url) + if err != nil { + log.Fatalf("HTTP GET: %v", err) + } + if resp.StatusCode != 200 { + log.Fatalf("Bad GET status for %q: %q", url, resp.Status) + } + return resp.Body +} + +// TODO: use Write*Version in all applicable packages. + +// WriteUnicodeVersion writes a constant for the Unicode version from which the +// tables are generated. +func WriteUnicodeVersion(w io.Writer) { + fmt.Fprintf(w, "// UnicodeVersion is the Unicode version from which the tables in this package are derived.\n") + fmt.Fprintf(w, "const UnicodeVersion = %q\n\n", UnicodeVersion()) +} + +// WriteCLDRVersion writes a constant for the CLDR version from which the +// tables are generated. +func WriteCLDRVersion(w io.Writer) { + fmt.Fprintf(w, "// CLDRVersion is the CLDR version from which the tables in this package are derived.\n") + fmt.Fprintf(w, "const CLDRVersion = %q\n\n", CLDRVersion()) +} + +// WriteGoFile prepends a standard file comment and package statement to the +// given bytes, applies gofmt, and writes them to a file with the given name. +// It will call log.Fatal if there are any errors. +func WriteGoFile(filename, pkg string, b []byte) { + w, err := os.Create(filename) + if err != nil { + log.Fatalf("Could not create file %s: %v", filename, err) + } + defer w.Close() + if _, err = WriteGo(w, pkg, "", b); err != nil { + log.Fatalf("Error writing file %s: %v", filename, err) + } +} + +func fileToPattern(filename string) string { + suffix := ".go" + if strings.HasSuffix(filename, "_test.go") { + suffix = "_test.go" + } + prefix := filename[:len(filename)-len(suffix)] + return fmt.Sprint(prefix, "%s", suffix) +} + +func updateBuildTags(pattern string) { + for _, t := range tags { + oldFile := fmt.Sprintf(pattern, t.version) + b, err := ioutil.ReadFile(oldFile) + if err != nil { + continue + } + build := fmt.Sprintf("// +build %s", t.buildTags) + b = regexp.MustCompile(`// \+build .*`).ReplaceAll(b, []byte(build)) + err = ioutil.WriteFile(oldFile, b, 0644) + if err != nil { + log.Fatal(err) + } + } +} + +// WriteVersionedGoFile prepends a standard file comment, adds build tags to +// version the file for the current Unicode version, and package statement to +// the given bytes, applies gofmt, and writes them to a file with the given +// name. It will call log.Fatal if there are any errors. +func WriteVersionedGoFile(filename, pkg string, b []byte) { + pattern := fileToPattern(filename) + updateBuildTags(pattern) + filename = fmt.Sprintf(pattern, UnicodeVersion()) + + w, err := os.Create(filename) + if err != nil { + log.Fatalf("Could not create file %s: %v", filename, err) + } + defer w.Close() + if _, err = WriteGo(w, pkg, buildTags(), b); err != nil { + log.Fatalf("Error writing file %s: %v", filename, err) + } +} + +// WriteGo prepends a standard file comment and package statement to the given +// bytes, applies gofmt, and writes them to w. +func WriteGo(w io.Writer, pkg, tags string, b []byte) (n int, err error) { + src := []byte(header) + if tags != "" { + src = append(src, fmt.Sprintf("// +build %s\n\n", tags)...) + } + src = append(src, fmt.Sprintf("package %s\n\n", pkg)...) + src = append(src, b...) + formatted, err := format.Source(src) + if err != nil { + // Print the generated code even in case of an error so that the + // returned error can be meaningfully interpreted. + n, _ = w.Write(src) + return n, err + } + return w.Write(formatted) +} + +// Repackage rewrites a Go file from belonging to package main to belonging to +// the given package. +func Repackage(inFile, outFile, pkg string) { + src, err := ioutil.ReadFile(inFile) + if err != nil { + log.Fatalf("reading %s: %v", inFile, err) + } + const toDelete = "package main\n\n" + i := bytes.Index(src, []byte(toDelete)) + if i < 0 { + log.Fatalf("Could not find %q in %s.", toDelete, inFile) + } + w := &bytes.Buffer{} + w.Write(src[i+len(toDelete):]) + WriteGoFile(outFile, pkg, w.Bytes()) +} diff --git a/vendor/golang.org/x/text/internal/triegen/compact.go b/vendor/golang.org/x/text/internal/triegen/compact.go new file mode 100644 index 0000000..397b975 --- /dev/null +++ b/vendor/golang.org/x/text/internal/triegen/compact.go @@ -0,0 +1,58 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package triegen + +// This file defines Compacter and its implementations. + +import "io" + +// A Compacter generates an alternative, more space-efficient way to store a +// trie value block. A trie value block holds all possible values for the last +// byte of a UTF-8 encoded rune. Excluding ASCII characters, a trie value block +// always has 64 values, as a UTF-8 encoding ends with a byte in [0x80, 0xC0). +type Compacter interface { + // Size returns whether the Compacter could encode the given block as well + // as its size in case it can. len(v) is always 64. + Size(v []uint64) (sz int, ok bool) + + // Store stores the block using the Compacter's compression method. + // It returns a handle with which the block can be retrieved. + // len(v) is always 64. + Store(v []uint64) uint32 + + // Print writes the data structures associated to the given store to w. + Print(w io.Writer) error + + // Handler returns the name of a function that gets called during trie + // lookup for blocks generated by the Compacter. The function should be of + // the form func (n uint32, b byte) uint64, where n is the index returned by + // the Compacter's Store method and b is the last byte of the UTF-8 + // encoding, where 0x80 <= b < 0xC0, for which to do the lookup in the + // block. + Handler() string +} + +// simpleCompacter is the default Compacter used by builder. It implements a +// normal trie block. +type simpleCompacter builder + +func (b *simpleCompacter) Size([]uint64) (sz int, ok bool) { + return blockSize * b.ValueSize, true +} + +func (b *simpleCompacter) Store(v []uint64) uint32 { + h := uint32(len(b.ValueBlocks) - blockOffset) + b.ValueBlocks = append(b.ValueBlocks, v) + return h +} + +func (b *simpleCompacter) Print(io.Writer) error { + // Structures are printed in print.go. + return nil +} + +func (b *simpleCompacter) Handler() string { + panic("Handler should be special-cased for this Compacter") +} diff --git a/vendor/golang.org/x/text/internal/triegen/print.go b/vendor/golang.org/x/text/internal/triegen/print.go new file mode 100644 index 0000000..8d9f120 --- /dev/null +++ b/vendor/golang.org/x/text/internal/triegen/print.go @@ -0,0 +1,251 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package triegen + +import ( + "bytes" + "fmt" + "io" + "strings" + "text/template" +) + +// print writes all the data structures as well as the code necessary to use the +// trie to w. +func (b *builder) print(w io.Writer) error { + b.Stats.NValueEntries = len(b.ValueBlocks) * blockSize + b.Stats.NValueBytes = len(b.ValueBlocks) * blockSize * b.ValueSize + b.Stats.NIndexEntries = len(b.IndexBlocks) * blockSize + b.Stats.NIndexBytes = len(b.IndexBlocks) * blockSize * b.IndexSize + b.Stats.NHandleBytes = len(b.Trie) * 2 * b.IndexSize + + // If we only have one root trie, all starter blocks are at position 0 and + // we can access the arrays directly. + if len(b.Trie) == 1 { + // At this point we cannot refer to the generated tables directly. + b.ASCIIBlock = b.Name + "Values" + b.StarterBlock = b.Name + "Index" + } else { + // Otherwise we need to have explicit starter indexes in the trie + // structure. + b.ASCIIBlock = "t.ascii" + b.StarterBlock = "t.utf8Start" + } + + b.SourceType = "[]byte" + if err := lookupGen.Execute(w, b); err != nil { + return err + } + + b.SourceType = "string" + if err := lookupGen.Execute(w, b); err != nil { + return err + } + + if err := trieGen.Execute(w, b); err != nil { + return err + } + + for _, c := range b.Compactions { + if err := c.c.Print(w); err != nil { + return err + } + } + + return nil +} + +func printValues(n int, values []uint64) string { + w := &bytes.Buffer{} + boff := n * blockSize + fmt.Fprintf(w, "\t// Block %#x, offset %#x", n, boff) + var newline bool + for i, v := range values { + if i%6 == 0 { + newline = true + } + if v != 0 { + if newline { + fmt.Fprintf(w, "\n") + newline = false + } + fmt.Fprintf(w, "\t%#02x:%#04x, ", boff+i, v) + } + } + return w.String() +} + +func printIndex(b *builder, nr int, n *node) string { + w := &bytes.Buffer{} + boff := nr * blockSize + fmt.Fprintf(w, "\t// Block %#x, offset %#x", nr, boff) + var newline bool + for i, c := range n.children { + if i%8 == 0 { + newline = true + } + if c != nil { + v := b.Compactions[c.index.compaction].Offset + uint32(c.index.index) + if v != 0 { + if newline { + fmt.Fprintf(w, "\n") + newline = false + } + fmt.Fprintf(w, "\t%#02x:%#02x, ", boff+i, v) + } + } + } + return w.String() +} + +var ( + trieGen = template.Must(template.New("trie").Funcs(template.FuncMap{ + "printValues": printValues, + "printIndex": printIndex, + "title": strings.Title, + "dec": func(x int) int { return x - 1 }, + "psize": func(n int) string { + return fmt.Sprintf("%d bytes (%.2f KiB)", n, float64(n)/1024) + }, + }).Parse(trieTemplate)) + lookupGen = template.Must(template.New("lookup").Parse(lookupTemplate)) +) + +// TODO: consider the return type of lookup. It could be uint64, even if the +// internal value type is smaller. We will have to verify this with the +// performance of unicode/norm, which is very sensitive to such changes. +const trieTemplate = `{{$b := .}}{{$multi := gt (len .Trie) 1}} +// {{.Name}}Trie. Total size: {{psize .Size}}. Checksum: {{printf "%08x" .Checksum}}. +type {{.Name}}Trie struct { {{if $multi}} + ascii []{{.ValueType}} // index for ASCII bytes + utf8Start []{{.IndexType}} // index for UTF-8 bytes >= 0xC0 +{{end}}} + +func new{{title .Name}}Trie(i int) *{{.Name}}Trie { {{if $multi}} + h := {{.Name}}TrieHandles[i] + return &{{.Name}}Trie{ {{.Name}}Values[uint32(h.ascii)<<6:], {{.Name}}Index[uint32(h.multi)<<6:] } +} + +type {{.Name}}TrieHandle struct { + ascii, multi {{.IndexType}} +} + +// {{.Name}}TrieHandles: {{len .Trie}} handles, {{.Stats.NHandleBytes}} bytes +var {{.Name}}TrieHandles = [{{len .Trie}}]{{.Name}}TrieHandle{ +{{range .Trie}} { {{.ASCIIIndex}}, {{.StarterIndex}} }, // {{printf "%08x" .Checksum}}: {{.Name}} +{{end}}}{{else}} + return &{{.Name}}Trie{} +} +{{end}} +// lookupValue determines the type of block n and looks up the value for b. +func (t *{{.Name}}Trie) lookupValue(n uint32, b byte) {{.ValueType}}{{$last := dec (len .Compactions)}} { + switch { {{range $i, $c := .Compactions}} + {{if eq $i $last}}default{{else}}case n < {{$c.Cutoff}}{{end}}:{{if ne $i 0}} + n -= {{$c.Offset}}{{end}} + return {{print $b.ValueType}}({{$c.Handler}}){{end}} + } +} + +// {{.Name}}Values: {{len .ValueBlocks}} blocks, {{.Stats.NValueEntries}} entries, {{.Stats.NValueBytes}} bytes +// The third block is the zero block. +var {{.Name}}Values = [{{.Stats.NValueEntries}}]{{.ValueType}} { +{{range $i, $v := .ValueBlocks}}{{printValues $i $v}} +{{end}}} + +// {{.Name}}Index: {{len .IndexBlocks}} blocks, {{.Stats.NIndexEntries}} entries, {{.Stats.NIndexBytes}} bytes +// Block 0 is the zero block. +var {{.Name}}Index = [{{.Stats.NIndexEntries}}]{{.IndexType}} { +{{range $i, $v := .IndexBlocks}}{{printIndex $b $i $v}} +{{end}}} +` + +// TODO: consider allowing zero-length strings after evaluating performance with +// unicode/norm. +const lookupTemplate = ` +// lookup{{if eq .SourceType "string"}}String{{end}} returns the trie value for the first UTF-8 encoding in s and +// the width in bytes of this encoding. The size will be 0 if s does not +// hold enough bytes to complete the encoding. len(s) must be greater than 0. +func (t *{{.Name}}Trie) lookup{{if eq .SourceType "string"}}String{{end}}(s {{.SourceType}}) (v {{.ValueType}}, sz int) { + c0 := s[0] + switch { + case c0 < 0x80: // is ASCII + return {{.ASCIIBlock}}[c0], 1 + case c0 < 0xC2: + return 0, 1 // Illegal UTF-8: not a starter, not ASCII. + case c0 < 0xE0: // 2-byte UTF-8 + if len(s) < 2 { + return 0, 0 + } + i := {{.StarterBlock}}[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c1), 2 + case c0 < 0xF0: // 3-byte UTF-8 + if len(s) < 3 { + return 0, 0 + } + i := {{.StarterBlock}}[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + o := uint32(i)<<6 + uint32(c1) + i = {{.Name}}Index[o] + c2 := s[2] + if c2 < 0x80 || 0xC0 <= c2 { + return 0, 2 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c2), 3 + case c0 < 0xF8: // 4-byte UTF-8 + if len(s) < 4 { + return 0, 0 + } + i := {{.StarterBlock}}[c0] + c1 := s[1] + if c1 < 0x80 || 0xC0 <= c1 { + return 0, 1 // Illegal UTF-8: not a continuation byte. + } + o := uint32(i)<<6 + uint32(c1) + i = {{.Name}}Index[o] + c2 := s[2] + if c2 < 0x80 || 0xC0 <= c2 { + return 0, 2 // Illegal UTF-8: not a continuation byte. + } + o = uint32(i)<<6 + uint32(c2) + i = {{.Name}}Index[o] + c3 := s[3] + if c3 < 0x80 || 0xC0 <= c3 { + return 0, 3 // Illegal UTF-8: not a continuation byte. + } + return t.lookupValue(uint32(i), c3), 4 + } + // Illegal rune + return 0, 1 +} + +// lookup{{if eq .SourceType "string"}}String{{end}}Unsafe returns the trie value for the first UTF-8 encoding in s. +// s must start with a full and valid UTF-8 encoded rune. +func (t *{{.Name}}Trie) lookup{{if eq .SourceType "string"}}String{{end}}Unsafe(s {{.SourceType}}) {{.ValueType}} { + c0 := s[0] + if c0 < 0x80 { // is ASCII + return {{.ASCIIBlock}}[c0] + } + i := {{.StarterBlock}}[c0] + if c0 < 0xE0 { // 2-byte UTF-8 + return t.lookupValue(uint32(i), s[1]) + } + i = {{.Name}}Index[uint32(i)<<6+uint32(s[1])] + if c0 < 0xF0 { // 3-byte UTF-8 + return t.lookupValue(uint32(i), s[2]) + } + i = {{.Name}}Index[uint32(i)<<6+uint32(s[2])] + if c0 < 0xF8 { // 4-byte UTF-8 + return t.lookupValue(uint32(i), s[3]) + } + return 0 +} +` diff --git a/vendor/golang.org/x/text/internal/triegen/triegen.go b/vendor/golang.org/x/text/internal/triegen/triegen.go new file mode 100644 index 0000000..51d218a --- /dev/null +++ b/vendor/golang.org/x/text/internal/triegen/triegen.go @@ -0,0 +1,494 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package triegen implements a code generator for a trie for associating +// unsigned integer values with UTF-8 encoded runes. +// +// Many of the go.text packages use tries for storing per-rune information. A +// trie is especially useful if many of the runes have the same value. If this +// is the case, many blocks can be expected to be shared allowing for +// information on many runes to be stored in little space. +// +// As most of the lookups are done directly on []byte slices, the tries use the +// UTF-8 bytes directly for the lookup. This saves a conversion from UTF-8 to +// runes and contributes a little bit to better performance. It also naturally +// provides a fast path for ASCII. +// +// Space is also an issue. There are many code points defined in Unicode and as +// a result tables can get quite large. So every byte counts. The triegen +// package automatically chooses the smallest integer values to represent the +// tables. Compacters allow further compression of the trie by allowing for +// alternative representations of individual trie blocks. +// +// triegen allows generating multiple tries as a single structure. This is +// useful when, for example, one wants to generate tries for several languages +// that have a lot of values in common. Some existing libraries for +// internationalization store all per-language data as a dynamically loadable +// chunk. The go.text packages are designed with the assumption that the user +// typically wants to compile in support for all supported languages, in line +// with the approach common to Go to create a single standalone binary. The +// multi-root trie approach can give significant storage savings in this +// scenario. +// +// triegen generates both tables and code. The code is optimized to use the +// automatically chosen data types. The following code is generated for a Trie +// or multiple Tries named "foo": +// - type fooTrie +// The trie type. +// +// - func newFooTrie(x int) *fooTrie +// Trie constructor, where x is the index of the trie passed to Gen. +// +// - func (t *fooTrie) lookup(s []byte) (v uintX, sz int) +// The lookup method, where uintX is automatically chosen. +// +// - func lookupString, lookupUnsafe and lookupStringUnsafe +// Variants of the above. +// +// - var fooValues and fooIndex and any tables generated by Compacters. +// The core trie data. +// +// - var fooTrieHandles +// Indexes of starter blocks in case of multiple trie roots. +// +// It is recommended that users test the generated trie by checking the returned +// value for every rune. Such exhaustive tests are possible as the number of +// runes in Unicode is limited. +package triegen // import "golang.org/x/text/internal/triegen" + +// TODO: Arguably, the internally optimized data types would not have to be +// exposed in the generated API. We could also investigate not generating the +// code, but using it through a package. We would have to investigate the impact +// on performance of making such change, though. For packages like unicode/norm, +// small changes like this could tank performance. + +import ( + "encoding/binary" + "fmt" + "hash/crc64" + "io" + "log" + "unicode/utf8" +) + +// builder builds a set of tries for associating values with runes. The set of +// tries can share common index and value blocks. +type builder struct { + Name string + + // ValueType is the type of the trie values looked up. + ValueType string + + // ValueSize is the byte size of the ValueType. + ValueSize int + + // IndexType is the type of trie index values used for all UTF-8 bytes of + // a rune except the last one. + IndexType string + + // IndexSize is the byte size of the IndexType. + IndexSize int + + // SourceType is used when generating the lookup functions. If the user + // requests StringSupport, all lookup functions will be generated for + // string input as well. + SourceType string + + Trie []*Trie + + IndexBlocks []*node + ValueBlocks [][]uint64 + Compactions []compaction + Checksum uint64 + + ASCIIBlock string + StarterBlock string + + indexBlockIdx map[uint64]int + valueBlockIdx map[uint64]nodeIndex + asciiBlockIdx map[uint64]int + + // Stats are used to fill out the template. + Stats struct { + NValueEntries int + NValueBytes int + NIndexEntries int + NIndexBytes int + NHandleBytes int + } + + err error +} + +// A nodeIndex encodes the index of a node, which is defined by the compaction +// which stores it and an index within the compaction. For internal nodes, the +// compaction is always 0. +type nodeIndex struct { + compaction int + index int +} + +// compaction keeps track of stats used for the compaction. +type compaction struct { + c Compacter + blocks []*node + maxHandle uint32 + totalSize int + + // Used by template-based generator and thus exported. + Cutoff uint32 + Offset uint32 + Handler string +} + +func (b *builder) setError(err error) { + if b.err == nil { + b.err = err + } +} + +// An Option can be passed to Gen. +type Option func(b *builder) error + +// Compact configures the trie generator to use the given Compacter. +func Compact(c Compacter) Option { + return func(b *builder) error { + b.Compactions = append(b.Compactions, compaction{ + c: c, + Handler: c.Handler() + "(n, b)"}) + return nil + } +} + +// Gen writes Go code for a shared trie lookup structure to w for the given +// Tries. The generated trie type will be called nameTrie. newNameTrie(x) will +// return the *nameTrie for tries[x]. A value can be looked up by using one of +// the various lookup methods defined on nameTrie. It returns the table size of +// the generated trie. +func Gen(w io.Writer, name string, tries []*Trie, opts ...Option) (sz int, err error) { + // The index contains two dummy blocks, followed by the zero block. The zero + // block is at offset 0x80, so that the offset for the zero block for + // continuation bytes is 0. + b := &builder{ + Name: name, + Trie: tries, + IndexBlocks: []*node{{}, {}, {}}, + Compactions: []compaction{{ + Handler: name + "Values[n<<6+uint32(b)]", + }}, + // The 0 key in indexBlockIdx and valueBlockIdx is the hash of the zero + // block. + indexBlockIdx: map[uint64]int{0: 0}, + valueBlockIdx: map[uint64]nodeIndex{0: {}}, + asciiBlockIdx: map[uint64]int{}, + } + b.Compactions[0].c = (*simpleCompacter)(b) + + for _, f := range opts { + if err := f(b); err != nil { + return 0, err + } + } + b.build() + if b.err != nil { + return 0, b.err + } + if err = b.print(w); err != nil { + return 0, err + } + return b.Size(), nil +} + +// A Trie represents a single root node of a trie. A builder may build several +// overlapping tries at once. +type Trie struct { + root *node + + hiddenTrie +} + +// hiddenTrie contains values we want to be visible to the template generator, +// but hidden from the API documentation. +type hiddenTrie struct { + Name string + Checksum uint64 + ASCIIIndex int + StarterIndex int +} + +// NewTrie returns a new trie root. +func NewTrie(name string) *Trie { + return &Trie{ + &node{ + children: make([]*node, blockSize), + values: make([]uint64, utf8.RuneSelf), + }, + hiddenTrie{Name: name}, + } +} + +// Gen is a convenience wrapper around the Gen func passing t as the only trie +// and uses the name passed to NewTrie. It returns the size of the generated +// tables. +func (t *Trie) Gen(w io.Writer, opts ...Option) (sz int, err error) { + return Gen(w, t.Name, []*Trie{t}, opts...) +} + +// node is a node of the intermediate trie structure. +type node struct { + // children holds this node's children. It is always of length 64. + // A child node may be nil. + children []*node + + // values contains the values of this node. If it is non-nil, this node is + // either a root or leaf node: + // For root nodes, len(values) == 128 and it maps the bytes in [0x00, 0x7F]. + // For leaf nodes, len(values) == 64 and it maps the bytes in [0x80, 0xBF]. + values []uint64 + + index nodeIndex +} + +// Insert associates value with the given rune. Insert will panic if a non-zero +// value is passed for an invalid rune. +func (t *Trie) Insert(r rune, value uint64) { + if value == 0 { + return + } + s := string(r) + if []rune(s)[0] != r && value != 0 { + // Note: The UCD tables will always assign what amounts to a zero value + // to a surrogate. Allowing a zero value for an illegal rune allows + // users to iterate over [0..MaxRune] without having to explicitly + // exclude surrogates, which would be tedious. + panic(fmt.Sprintf("triegen: non-zero value for invalid rune %U", r)) + } + if len(s) == 1 { + // It is a root node value (ASCII). + t.root.values[s[0]] = value + return + } + + n := t.root + for ; len(s) > 1; s = s[1:] { + if n.children == nil { + n.children = make([]*node, blockSize) + } + p := s[0] % blockSize + c := n.children[p] + if c == nil { + c = &node{} + n.children[p] = c + } + if len(s) > 2 && c.values != nil { + log.Fatalf("triegen: insert(%U): found internal node with values", r) + } + n = c + } + if n.values == nil { + n.values = make([]uint64, blockSize) + } + if n.children != nil { + log.Fatalf("triegen: insert(%U): found leaf node that also has child nodes", r) + } + n.values[s[0]-0x80] = value +} + +// Size returns the number of bytes the generated trie will take to store. It +// needs to be exported as it is used in the templates. +func (b *builder) Size() int { + // Index blocks. + sz := len(b.IndexBlocks) * blockSize * b.IndexSize + + // Skip the first compaction, which represents the normal value blocks, as + // its totalSize does not account for the ASCII blocks, which are managed + // separately. + sz += len(b.ValueBlocks) * blockSize * b.ValueSize + for _, c := range b.Compactions[1:] { + sz += c.totalSize + } + + // TODO: this computation does not account for the fixed overhead of a using + // a compaction, either code or data. As for data, though, the typical + // overhead of data is in the order of bytes (2 bytes for cases). Further, + // the savings of using a compaction should anyway be substantial for it to + // be worth it. + + // For multi-root tries, we also need to account for the handles. + if len(b.Trie) > 1 { + sz += 2 * b.IndexSize * len(b.Trie) + } + return sz +} + +func (b *builder) build() { + // Compute the sizes of the values. + var vmax uint64 + for _, t := range b.Trie { + vmax = maxValue(t.root, vmax) + } + b.ValueType, b.ValueSize = getIntType(vmax) + + // Compute all block allocations. + // TODO: first compute the ASCII blocks for all tries and then the other + // nodes. ASCII blocks are more restricted in placement, as they require two + // blocks to be placed consecutively. Processing them first may improve + // sharing (at least one zero block can be expected to be saved.) + for _, t := range b.Trie { + b.Checksum += b.buildTrie(t) + } + + // Compute the offsets for all the Compacters. + offset := uint32(0) + for i := range b.Compactions { + c := &b.Compactions[i] + c.Offset = offset + offset += c.maxHandle + 1 + c.Cutoff = offset + } + + // Compute the sizes of indexes. + // TODO: different byte positions could have different sizes. So far we have + // not found a case where this is beneficial. + imax := uint64(b.Compactions[len(b.Compactions)-1].Cutoff) + for _, ib := range b.IndexBlocks { + if x := uint64(ib.index.index); x > imax { + imax = x + } + } + b.IndexType, b.IndexSize = getIntType(imax) +} + +func maxValue(n *node, max uint64) uint64 { + if n == nil { + return max + } + for _, c := range n.children { + max = maxValue(c, max) + } + for _, v := range n.values { + if max < v { + max = v + } + } + return max +} + +func getIntType(v uint64) (string, int) { + switch { + case v < 1<<8: + return "uint8", 1 + case v < 1<<16: + return "uint16", 2 + case v < 1<<32: + return "uint32", 4 + } + return "uint64", 8 +} + +const ( + blockSize = 64 + + // Subtract two blocks to offset 0x80, the first continuation byte. + blockOffset = 2 + + // Subtract three blocks to offset 0xC0, the first non-ASCII starter. + rootBlockOffset = 3 +) + +var crcTable = crc64.MakeTable(crc64.ISO) + +func (b *builder) buildTrie(t *Trie) uint64 { + n := t.root + + // Get the ASCII offset. For the first trie, the ASCII block will be at + // position 0. + hasher := crc64.New(crcTable) + binary.Write(hasher, binary.BigEndian, n.values) + hash := hasher.Sum64() + + v, ok := b.asciiBlockIdx[hash] + if !ok { + v = len(b.ValueBlocks) + b.asciiBlockIdx[hash] = v + + b.ValueBlocks = append(b.ValueBlocks, n.values[:blockSize], n.values[blockSize:]) + if v == 0 { + // Add the zero block at position 2 so that it will be assigned a + // zero reference in the lookup blocks. + // TODO: always do this? This would allow us to remove a check from + // the trie lookup, but at the expense of extra space. Analyze + // performance for unicode/norm. + b.ValueBlocks = append(b.ValueBlocks, make([]uint64, blockSize)) + } + } + t.ASCIIIndex = v + + // Compute remaining offsets. + t.Checksum = b.computeOffsets(n, true) + // We already subtracted the normal blockOffset from the index. Subtract the + // difference for starter bytes. + t.StarterIndex = n.index.index - (rootBlockOffset - blockOffset) + return t.Checksum +} + +func (b *builder) computeOffsets(n *node, root bool) uint64 { + // For the first trie, the root lookup block will be at position 3, which is + // the offset for UTF-8 non-ASCII starter bytes. + first := len(b.IndexBlocks) == rootBlockOffset + if first { + b.IndexBlocks = append(b.IndexBlocks, n) + } + + // We special-case the cases where all values recursively are 0. This allows + // for the use of a zero block to which all such values can be directed. + hash := uint64(0) + if n.children != nil || n.values != nil { + hasher := crc64.New(crcTable) + for _, c := range n.children { + var v uint64 + if c != nil { + v = b.computeOffsets(c, false) + } + binary.Write(hasher, binary.BigEndian, v) + } + binary.Write(hasher, binary.BigEndian, n.values) + hash = hasher.Sum64() + } + + if first { + b.indexBlockIdx[hash] = rootBlockOffset - blockOffset + } + + // Compacters don't apply to internal nodes. + if n.children != nil { + v, ok := b.indexBlockIdx[hash] + if !ok { + v = len(b.IndexBlocks) - blockOffset + b.IndexBlocks = append(b.IndexBlocks, n) + b.indexBlockIdx[hash] = v + } + n.index = nodeIndex{0, v} + } else { + h, ok := b.valueBlockIdx[hash] + if !ok { + bestI, bestSize := 0, blockSize*b.ValueSize + for i, c := range b.Compactions[1:] { + if sz, ok := c.c.Size(n.values); ok && bestSize > sz { + bestI, bestSize = i+1, sz + } + } + c := &b.Compactions[bestI] + c.totalSize += bestSize + v := c.c.Store(n.values) + if c.maxHandle < v { + c.maxHandle = v + } + h = nodeIndex{bestI, int(v)} + b.valueBlockIdx[hash] = h + } + n.index = h + } + return hash +} diff --git a/vendor/golang.org/x/text/internal/ucd/ucd.go b/vendor/golang.org/x/text/internal/ucd/ucd.go new file mode 100644 index 0000000..0879bc8 --- /dev/null +++ b/vendor/golang.org/x/text/internal/ucd/ucd.go @@ -0,0 +1,371 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package ucd provides a parser for Unicode Character Database files, the +// format of which is defined in https://www.unicode.org/reports/tr44/. See +// https://www.unicode.org/Public/UCD/latest/ucd/ for example files. +// +// It currently does not support substitutions of missing fields. +package ucd // import "golang.org/x/text/internal/ucd" + +import ( + "bufio" + "errors" + "fmt" + "io" + "log" + "regexp" + "strconv" + "strings" +) + +// UnicodeData.txt fields. +const ( + CodePoint = iota + Name + GeneralCategory + CanonicalCombiningClass + BidiClass + DecompMapping + DecimalValue + DigitValue + NumericValue + BidiMirrored + Unicode1Name + ISOComment + SimpleUppercaseMapping + SimpleLowercaseMapping + SimpleTitlecaseMapping +) + +// Parse calls f for each entry in the given reader of a UCD file. It will close +// the reader upon return. It will call log.Fatal if any error occurred. +// +// This implements the most common usage pattern of using Parser. +func Parse(r io.ReadCloser, f func(p *Parser)) { + defer r.Close() + + p := New(r) + for p.Next() { + f(p) + } + if err := p.Err(); err != nil { + r.Close() // os.Exit will cause defers not to be called. + log.Fatal(err) + } +} + +// An Option is used to configure a Parser. +type Option func(p *Parser) + +func keepRanges(p *Parser) { + p.keepRanges = true +} + +var ( + // KeepRanges prevents the expansion of ranges. The raw ranges can be + // obtained by calling Range(0) on the parser. + KeepRanges Option = keepRanges +) + +// The Part option register a handler for lines starting with a '@'. The text +// after a '@' is available as the first field. Comments are handled as usual. +func Part(f func(p *Parser)) Option { + return func(p *Parser) { + p.partHandler = f + } +} + +// The CommentHandler option passes comments that are on a line by itself to +// a given handler. +func CommentHandler(f func(s string)) Option { + return func(p *Parser) { + p.commentHandler = f + } +} + +// A Parser parses Unicode Character Database (UCD) files. +type Parser struct { + scanner *bufio.Scanner + + keepRanges bool // Don't expand rune ranges in field 0. + + err error + comment string + field []string + // parsedRange is needed in case Range(0) is called more than once for one + // field. In some cases this requires scanning ahead. + line int + parsedRange bool + rangeStart, rangeEnd rune + + partHandler func(p *Parser) + commentHandler func(s string) +} + +func (p *Parser) setError(err error, msg string) { + if p.err == nil && err != nil { + if msg == "" { + p.err = fmt.Errorf("ucd:line:%d: %v", p.line, err) + } else { + p.err = fmt.Errorf("ucd:line:%d:%s: %v", p.line, msg, err) + } + } +} + +func (p *Parser) getField(i int) string { + if i >= len(p.field) { + return "" + } + return p.field[i] +} + +// Err returns a non-nil error if any error occurred during parsing. +func (p *Parser) Err() error { + return p.err +} + +// New returns a Parser for the given Reader. +func New(r io.Reader, o ...Option) *Parser { + p := &Parser{ + scanner: bufio.NewScanner(r), + } + for _, f := range o { + f(p) + } + return p +} + +// Next parses the next line in the file. It returns true if a line was parsed +// and false if it reached the end of the file. +func (p *Parser) Next() bool { + if !p.keepRanges && p.rangeStart < p.rangeEnd { + p.rangeStart++ + return true + } + p.comment = "" + p.field = p.field[:0] + p.parsedRange = false + + for p.scanner.Scan() && p.err == nil { + p.line++ + s := p.scanner.Text() + if s == "" { + continue + } + if s[0] == '#' { + if p.commentHandler != nil { + p.commentHandler(strings.TrimSpace(s[1:])) + } + continue + } + + // Parse line + if i := strings.IndexByte(s, '#'); i != -1 { + p.comment = strings.TrimSpace(s[i+1:]) + s = s[:i] + } + if s[0] == '@' { + if p.partHandler != nil { + p.field = append(p.field, strings.TrimSpace(s[1:])) + p.partHandler(p) + p.field = p.field[:0] + } + p.comment = "" + continue + } + for { + i := strings.IndexByte(s, ';') + if i == -1 { + p.field = append(p.field, strings.TrimSpace(s)) + break + } + p.field = append(p.field, strings.TrimSpace(s[:i])) + s = s[i+1:] + } + if !p.keepRanges { + p.rangeStart, p.rangeEnd = p.getRange(0) + } + return true + } + p.setError(p.scanner.Err(), "scanner failed") + return false +} + +func parseRune(b string) (rune, error) { + if len(b) > 2 && b[0] == 'U' && b[1] == '+' { + b = b[2:] + } + x, err := strconv.ParseUint(b, 16, 32) + return rune(x), err +} + +func (p *Parser) parseRune(s string) rune { + x, err := parseRune(s) + p.setError(err, "failed to parse rune") + return x +} + +// Rune parses and returns field i as a rune. +func (p *Parser) Rune(i int) rune { + if i > 0 || p.keepRanges { + return p.parseRune(p.getField(i)) + } + return p.rangeStart +} + +// Runes interprets and returns field i as a sequence of runes. +func (p *Parser) Runes(i int) (runes []rune) { + add := func(s string) { + if s = strings.TrimSpace(s); len(s) > 0 { + runes = append(runes, p.parseRune(s)) + } + } + for b := p.getField(i); ; { + i := strings.IndexByte(b, ' ') + if i == -1 { + add(b) + break + } + add(b[:i]) + b = b[i+1:] + } + return +} + +var ( + errIncorrectLegacyRange = errors.New("ucd: unmatched <* First>") + + // reRange matches one line of a legacy rune range. + reRange = regexp.MustCompile("^([0-9A-F]*);<([^,]*), ([^>]*)>(.*)$") +) + +// Range parses and returns field i as a rune range. A range is inclusive at +// both ends. If the field only has one rune, first and last will be identical. +// It supports the legacy format for ranges used in UnicodeData.txt. +func (p *Parser) Range(i int) (first, last rune) { + if !p.keepRanges { + return p.rangeStart, p.rangeStart + } + return p.getRange(i) +} + +func (p *Parser) getRange(i int) (first, last rune) { + b := p.getField(i) + if k := strings.Index(b, ".."); k != -1 { + return p.parseRune(b[:k]), p.parseRune(b[k+2:]) + } + // The first field may not be a rune, in which case we may ignore any error + // and set the range as 0..0. + x, err := parseRune(b) + if err != nil { + // Disable range parsing henceforth. This ensures that an error will be + // returned if the user subsequently will try to parse this field as + // a Rune. + p.keepRanges = true + } + // Special case for UnicodeData that was retained for backwards compatibility. + if i == 0 && len(p.field) > 1 && strings.HasSuffix(p.field[1], "First>") { + if p.parsedRange { + return p.rangeStart, p.rangeEnd + } + mf := reRange.FindStringSubmatch(p.scanner.Text()) + p.line++ + if mf == nil || !p.scanner.Scan() { + p.setError(errIncorrectLegacyRange, "") + return x, x + } + // Using Bytes would be more efficient here, but Text is a lot easier + // and this is not a frequent case. + ml := reRange.FindStringSubmatch(p.scanner.Text()) + if ml == nil || mf[2] != ml[2] || ml[3] != "Last" || mf[4] != ml[4] { + p.setError(errIncorrectLegacyRange, "") + return x, x + } + p.rangeStart, p.rangeEnd = x, p.parseRune(p.scanner.Text()[:len(ml[1])]) + p.parsedRange = true + return p.rangeStart, p.rangeEnd + } + return x, x +} + +// bools recognizes all valid UCD boolean values. +var bools = map[string]bool{ + "": false, + "N": false, + "No": false, + "F": false, + "False": false, + "Y": true, + "Yes": true, + "T": true, + "True": true, +} + +// Bool parses and returns field i as a boolean value. +func (p *Parser) Bool(i int) bool { + f := p.getField(i) + for s, v := range bools { + if f == s { + return v + } + } + p.setError(strconv.ErrSyntax, "error parsing bool") + return false +} + +// Int parses and returns field i as an integer value. +func (p *Parser) Int(i int) int { + x, err := strconv.ParseInt(string(p.getField(i)), 10, 64) + p.setError(err, "error parsing int") + return int(x) +} + +// Uint parses and returns field i as an unsigned integer value. +func (p *Parser) Uint(i int) uint { + x, err := strconv.ParseUint(string(p.getField(i)), 10, 64) + p.setError(err, "error parsing uint") + return uint(x) +} + +// Float parses and returns field i as a decimal value. +func (p *Parser) Float(i int) float64 { + x, err := strconv.ParseFloat(string(p.getField(i)), 64) + p.setError(err, "error parsing float") + return x +} + +// String parses and returns field i as a string value. +func (p *Parser) String(i int) string { + return string(p.getField(i)) +} + +// Strings parses and returns field i as a space-separated list of strings. +func (p *Parser) Strings(i int) []string { + ss := strings.Split(string(p.getField(i)), " ") + for i, s := range ss { + ss[i] = strings.TrimSpace(s) + } + return ss +} + +// Comment returns the comments for the current line. +func (p *Parser) Comment() string { + return string(p.comment) +} + +var errUndefinedEnum = errors.New("ucd: undefined enum value") + +// Enum interprets and returns field i as a value that must be one of the values +// in enum. +func (p *Parser) Enum(i int, enum ...string) string { + f := p.getField(i) + for _, s := range enum { + if f == s { + return s + } + } + p.setError(errUndefinedEnum, "error parsing enum") + return "" +} diff --git a/vendor/golang.org/x/text/transform/transform.go b/vendor/golang.org/x/text/transform/transform.go new file mode 100644 index 0000000..48ec64b --- /dev/null +++ b/vendor/golang.org/x/text/transform/transform.go @@ -0,0 +1,709 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package transform provides reader and writer wrappers that transform the +// bytes passing through as well as various transformations. Example +// transformations provided by other packages include normalization and +// conversion between character sets. +package transform // import "golang.org/x/text/transform" + +import ( + "bytes" + "errors" + "io" + "unicode/utf8" +) + +var ( + // ErrShortDst means that the destination buffer was too short to + // receive all of the transformed bytes. + ErrShortDst = errors.New("transform: short destination buffer") + + // ErrShortSrc means that the source buffer has insufficient data to + // complete the transformation. + ErrShortSrc = errors.New("transform: short source buffer") + + // ErrEndOfSpan means that the input and output (the transformed input) + // are not identical. + ErrEndOfSpan = errors.New("transform: input and output are not identical") + + // errInconsistentByteCount means that Transform returned success (nil + // error) but also returned nSrc inconsistent with the src argument. + errInconsistentByteCount = errors.New("transform: inconsistent byte count returned") + + // errShortInternal means that an internal buffer is not large enough + // to make progress and the Transform operation must be aborted. + errShortInternal = errors.New("transform: short internal buffer") +) + +// Transformer transforms bytes. +type Transformer interface { + // Transform writes to dst the transformed bytes read from src, and + // returns the number of dst bytes written and src bytes read. The + // atEOF argument tells whether src represents the last bytes of the + // input. + // + // Callers should always process the nDst bytes produced and account + // for the nSrc bytes consumed before considering the error err. + // + // A nil error means that all of the transformed bytes (whether freshly + // transformed from src or left over from previous Transform calls) + // were written to dst. A nil error can be returned regardless of + // whether atEOF is true. If err is nil then nSrc must equal len(src); + // the converse is not necessarily true. + // + // ErrShortDst means that dst was too short to receive all of the + // transformed bytes. ErrShortSrc means that src had insufficient data + // to complete the transformation. If both conditions apply, then + // either error may be returned. Other than the error conditions listed + // here, implementations are free to report other errors that arise. + Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) + + // Reset resets the state and allows a Transformer to be reused. + Reset() +} + +// SpanningTransformer extends the Transformer interface with a Span method +// that determines how much of the input already conforms to the Transformer. +type SpanningTransformer interface { + Transformer + + // Span returns a position in src such that transforming src[:n] results in + // identical output src[:n] for these bytes. It does not necessarily return + // the largest such n. The atEOF argument tells whether src represents the + // last bytes of the input. + // + // Callers should always account for the n bytes consumed before + // considering the error err. + // + // A nil error means that all input bytes are known to be identical to the + // output produced by the Transformer. A nil error can be returned + // regardless of whether atEOF is true. If err is nil, then n must + // equal len(src); the converse is not necessarily true. + // + // ErrEndOfSpan means that the Transformer output may differ from the + // input after n bytes. Note that n may be len(src), meaning that the output + // would contain additional bytes after otherwise identical output. + // ErrShortSrc means that src had insufficient data to determine whether the + // remaining bytes would change. Other than the error conditions listed + // here, implementations are free to report other errors that arise. + // + // Calling Span can modify the Transformer state as a side effect. In + // effect, it does the transformation just as calling Transform would, only + // without copying to a destination buffer and only up to a point it can + // determine the input and output bytes are the same. This is obviously more + // limited than calling Transform, but can be more efficient in terms of + // copying and allocating buffers. Calls to Span and Transform may be + // interleaved. + Span(src []byte, atEOF bool) (n int, err error) +} + +// NopResetter can be embedded by implementations of Transformer to add a nop +// Reset method. +type NopResetter struct{} + +// Reset implements the Reset method of the Transformer interface. +func (NopResetter) Reset() {} + +// Reader wraps another io.Reader by transforming the bytes read. +type Reader struct { + r io.Reader + t Transformer + err error + + // dst[dst0:dst1] contains bytes that have been transformed by t but + // not yet copied out via Read. + dst []byte + dst0, dst1 int + + // src[src0:src1] contains bytes that have been read from r but not + // yet transformed through t. + src []byte + src0, src1 int + + // transformComplete is whether the transformation is complete, + // regardless of whether or not it was successful. + transformComplete bool +} + +const defaultBufSize = 4096 + +// NewReader returns a new Reader that wraps r by transforming the bytes read +// via t. It calls Reset on t. +func NewReader(r io.Reader, t Transformer) *Reader { + t.Reset() + return &Reader{ + r: r, + t: t, + dst: make([]byte, defaultBufSize), + src: make([]byte, defaultBufSize), + } +} + +// Read implements the io.Reader interface. +func (r *Reader) Read(p []byte) (int, error) { + n, err := 0, error(nil) + for { + // Copy out any transformed bytes and return the final error if we are done. + if r.dst0 != r.dst1 { + n = copy(p, r.dst[r.dst0:r.dst1]) + r.dst0 += n + if r.dst0 == r.dst1 && r.transformComplete { + return n, r.err + } + return n, nil + } else if r.transformComplete { + return 0, r.err + } + + // Try to transform some source bytes, or to flush the transformer if we + // are out of source bytes. We do this even if r.r.Read returned an error. + // As the io.Reader documentation says, "process the n > 0 bytes returned + // before considering the error". + if r.src0 != r.src1 || r.err != nil { + r.dst0 = 0 + r.dst1, n, err = r.t.Transform(r.dst, r.src[r.src0:r.src1], r.err == io.EOF) + r.src0 += n + + switch { + case err == nil: + if r.src0 != r.src1 { + r.err = errInconsistentByteCount + } + // The Transform call was successful; we are complete if we + // cannot read more bytes into src. + r.transformComplete = r.err != nil + continue + case err == ErrShortDst && (r.dst1 != 0 || n != 0): + // Make room in dst by copying out, and try again. + continue + case err == ErrShortSrc && r.src1-r.src0 != len(r.src) && r.err == nil: + // Read more bytes into src via the code below, and try again. + default: + r.transformComplete = true + // The reader error (r.err) takes precedence over the + // transformer error (err) unless r.err is nil or io.EOF. + if r.err == nil || r.err == io.EOF { + r.err = err + } + continue + } + } + + // Move any untransformed source bytes to the start of the buffer + // and read more bytes. + if r.src0 != 0 { + r.src0, r.src1 = 0, copy(r.src, r.src[r.src0:r.src1]) + } + n, r.err = r.r.Read(r.src[r.src1:]) + r.src1 += n + } +} + +// TODO: implement ReadByte (and ReadRune??). + +// Writer wraps another io.Writer by transforming the bytes read. +// The user needs to call Close to flush unwritten bytes that may +// be buffered. +type Writer struct { + w io.Writer + t Transformer + dst []byte + + // src[:n] contains bytes that have not yet passed through t. + src []byte + n int +} + +// NewWriter returns a new Writer that wraps w by transforming the bytes written +// via t. It calls Reset on t. +func NewWriter(w io.Writer, t Transformer) *Writer { + t.Reset() + return &Writer{ + w: w, + t: t, + dst: make([]byte, defaultBufSize), + src: make([]byte, defaultBufSize), + } +} + +// Write implements the io.Writer interface. If there are not enough +// bytes available to complete a Transform, the bytes will be buffered +// for the next write. Call Close to convert the remaining bytes. +func (w *Writer) Write(data []byte) (n int, err error) { + src := data + if w.n > 0 { + // Append bytes from data to the last remainder. + // TODO: limit the amount copied on first try. + n = copy(w.src[w.n:], data) + w.n += n + src = w.src[:w.n] + } + for { + nDst, nSrc, err := w.t.Transform(w.dst, src, false) + if _, werr := w.w.Write(w.dst[:nDst]); werr != nil { + return n, werr + } + src = src[nSrc:] + if w.n == 0 { + n += nSrc + } else if len(src) <= n { + // Enough bytes from w.src have been consumed. We make src point + // to data instead to reduce the copying. + w.n = 0 + n -= len(src) + src = data[n:] + if n < len(data) && (err == nil || err == ErrShortSrc) { + continue + } + } + switch err { + case ErrShortDst: + // This error is okay as long as we are making progress. + if nDst > 0 || nSrc > 0 { + continue + } + case ErrShortSrc: + if len(src) < len(w.src) { + m := copy(w.src, src) + // If w.n > 0, bytes from data were already copied to w.src and n + // was already set to the number of bytes consumed. + if w.n == 0 { + n += m + } + w.n = m + err = nil + } else if nDst > 0 || nSrc > 0 { + // Not enough buffer to store the remainder. Keep processing as + // long as there is progress. Without this case, transforms that + // require a lookahead larger than the buffer may result in an + // error. This is not something one may expect to be common in + // practice, but it may occur when buffers are set to small + // sizes during testing. + continue + } + case nil: + if w.n > 0 { + err = errInconsistentByteCount + } + } + return n, err + } +} + +// Close implements the io.Closer interface. +func (w *Writer) Close() error { + src := w.src[:w.n] + for { + nDst, nSrc, err := w.t.Transform(w.dst, src, true) + if _, werr := w.w.Write(w.dst[:nDst]); werr != nil { + return werr + } + if err != ErrShortDst { + return err + } + src = src[nSrc:] + } +} + +type nop struct{ NopResetter } + +func (nop) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + n := copy(dst, src) + if n < len(src) { + err = ErrShortDst + } + return n, n, err +} + +func (nop) Span(src []byte, atEOF bool) (n int, err error) { + return len(src), nil +} + +type discard struct{ NopResetter } + +func (discard) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + return 0, len(src), nil +} + +var ( + // Discard is a Transformer for which all Transform calls succeed + // by consuming all bytes and writing nothing. + Discard Transformer = discard{} + + // Nop is a SpanningTransformer that copies src to dst. + Nop SpanningTransformer = nop{} +) + +// chain is a sequence of links. A chain with N Transformers has N+1 links and +// N+1 buffers. Of those N+1 buffers, the first and last are the src and dst +// buffers given to chain.Transform and the middle N-1 buffers are intermediate +// buffers owned by the chain. The i'th link transforms bytes from the i'th +// buffer chain.link[i].b at read offset chain.link[i].p to the i+1'th buffer +// chain.link[i+1].b at write offset chain.link[i+1].n, for i in [0, N). +type chain struct { + link []link + err error + // errStart is the index at which the error occurred plus 1. Processing + // errStart at this level at the next call to Transform. As long as + // errStart > 0, chain will not consume any more source bytes. + errStart int +} + +func (c *chain) fatalError(errIndex int, err error) { + if i := errIndex + 1; i > c.errStart { + c.errStart = i + c.err = err + } +} + +type link struct { + t Transformer + // b[p:n] holds the bytes to be transformed by t. + b []byte + p int + n int +} + +func (l *link) src() []byte { + return l.b[l.p:l.n] +} + +func (l *link) dst() []byte { + return l.b[l.n:] +} + +// Chain returns a Transformer that applies t in sequence. +func Chain(t ...Transformer) Transformer { + if len(t) == 0 { + return nop{} + } + c := &chain{link: make([]link, len(t)+1)} + for i, tt := range t { + c.link[i].t = tt + } + // Allocate intermediate buffers. + b := make([][defaultBufSize]byte, len(t)-1) + for i := range b { + c.link[i+1].b = b[i][:] + } + return c +} + +// Reset resets the state of Chain. It calls Reset on all the Transformers. +func (c *chain) Reset() { + for i, l := range c.link { + if l.t != nil { + l.t.Reset() + } + c.link[i].p, c.link[i].n = 0, 0 + } +} + +// TODO: make chain use Span (is going to be fun to implement!) + +// Transform applies the transformers of c in sequence. +func (c *chain) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + // Set up src and dst in the chain. + srcL := &c.link[0] + dstL := &c.link[len(c.link)-1] + srcL.b, srcL.p, srcL.n = src, 0, len(src) + dstL.b, dstL.n = dst, 0 + var lastFull, needProgress bool // for detecting progress + + // i is the index of the next Transformer to apply, for i in [low, high]. + // low is the lowest index for which c.link[low] may still produce bytes. + // high is the highest index for which c.link[high] has a Transformer. + // The error returned by Transform determines whether to increase or + // decrease i. We try to completely fill a buffer before converting it. + for low, i, high := c.errStart, c.errStart, len(c.link)-2; low <= i && i <= high; { + in, out := &c.link[i], &c.link[i+1] + nDst, nSrc, err0 := in.t.Transform(out.dst(), in.src(), atEOF && low == i) + out.n += nDst + in.p += nSrc + if i > 0 && in.p == in.n { + in.p, in.n = 0, 0 + } + needProgress, lastFull = lastFull, false + switch err0 { + case ErrShortDst: + // Process the destination buffer next. Return if we are already + // at the high index. + if i == high { + return dstL.n, srcL.p, ErrShortDst + } + if out.n != 0 { + i++ + // If the Transformer at the next index is not able to process any + // source bytes there is nothing that can be done to make progress + // and the bytes will remain unprocessed. lastFull is used to + // detect this and break out of the loop with a fatal error. + lastFull = true + continue + } + // The destination buffer was too small, but is completely empty. + // Return a fatal error as this transformation can never complete. + c.fatalError(i, errShortInternal) + case ErrShortSrc: + if i == 0 { + // Save ErrShortSrc in err. All other errors take precedence. + err = ErrShortSrc + break + } + // Source bytes were depleted before filling up the destination buffer. + // Verify we made some progress, move the remaining bytes to the errStart + // and try to get more source bytes. + if needProgress && nSrc == 0 || in.n-in.p == len(in.b) { + // There were not enough source bytes to proceed while the source + // buffer cannot hold any more bytes. Return a fatal error as this + // transformation can never complete. + c.fatalError(i, errShortInternal) + break + } + // in.b is an internal buffer and we can make progress. + in.p, in.n = 0, copy(in.b, in.src()) + fallthrough + case nil: + // if i == low, we have depleted the bytes at index i or any lower levels. + // In that case we increase low and i. In all other cases we decrease i to + // fetch more bytes before proceeding to the next index. + if i > low { + i-- + continue + } + default: + c.fatalError(i, err0) + } + // Exhausted level low or fatal error: increase low and continue + // to process the bytes accepted so far. + i++ + low = i + } + + // If c.errStart > 0, this means we found a fatal error. We will clear + // all upstream buffers. At this point, no more progress can be made + // downstream, as Transform would have bailed while handling ErrShortDst. + if c.errStart > 0 { + for i := 1; i < c.errStart; i++ { + c.link[i].p, c.link[i].n = 0, 0 + } + err, c.errStart, c.err = c.err, 0, nil + } + return dstL.n, srcL.p, err +} + +// Deprecated: Use runes.Remove instead. +func RemoveFunc(f func(r rune) bool) Transformer { + return removeF(f) +} + +type removeF func(r rune) bool + +func (removeF) Reset() {} + +// Transform implements the Transformer interface. +func (t removeF) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + for r, sz := rune(0), 0; len(src) > 0; src = src[sz:] { + + if r = rune(src[0]); r < utf8.RuneSelf { + sz = 1 + } else { + r, sz = utf8.DecodeRune(src) + + if sz == 1 { + // Invalid rune. + if !atEOF && !utf8.FullRune(src) { + err = ErrShortSrc + break + } + // We replace illegal bytes with RuneError. Not doing so might + // otherwise turn a sequence of invalid UTF-8 into valid UTF-8. + // The resulting byte sequence may subsequently contain runes + // for which t(r) is true that were passed unnoticed. + if !t(r) { + if nDst+3 > len(dst) { + err = ErrShortDst + break + } + nDst += copy(dst[nDst:], "\uFFFD") + } + nSrc++ + continue + } + } + + if !t(r) { + if nDst+sz > len(dst) { + err = ErrShortDst + break + } + nDst += copy(dst[nDst:], src[:sz]) + } + nSrc += sz + } + return +} + +// grow returns a new []byte that is longer than b, and copies the first n bytes +// of b to the start of the new slice. +func grow(b []byte, n int) []byte { + m := len(b) + if m <= 32 { + m = 64 + } else if m <= 256 { + m *= 2 + } else { + m += m >> 1 + } + buf := make([]byte, m) + copy(buf, b[:n]) + return buf +} + +const initialBufSize = 128 + +// String returns a string with the result of converting s[:n] using t, where +// n <= len(s). If err == nil, n will be len(s). It calls Reset on t. +func String(t Transformer, s string) (result string, n int, err error) { + t.Reset() + if s == "" { + // Fast path for the common case for empty input. Results in about a + // 86% reduction of running time for BenchmarkStringLowerEmpty. + if _, _, err := t.Transform(nil, nil, true); err == nil { + return "", 0, nil + } + } + + // Allocate only once. Note that both dst and src escape when passed to + // Transform. + buf := [2 * initialBufSize]byte{} + dst := buf[:initialBufSize:initialBufSize] + src := buf[initialBufSize : 2*initialBufSize] + + // The input string s is transformed in multiple chunks (starting with a + // chunk size of initialBufSize). nDst and nSrc are per-chunk (or + // per-Transform-call) indexes, pDst and pSrc are overall indexes. + nDst, nSrc := 0, 0 + pDst, pSrc := 0, 0 + + // pPrefix is the length of a common prefix: the first pPrefix bytes of the + // result will equal the first pPrefix bytes of s. It is not guaranteed to + // be the largest such value, but if pPrefix, len(result) and len(s) are + // all equal after the final transform (i.e. calling Transform with atEOF + // being true returned nil error) then we don't need to allocate a new + // result string. + pPrefix := 0 + for { + // Invariant: pDst == pPrefix && pSrc == pPrefix. + + n := copy(src, s[pSrc:]) + nDst, nSrc, err = t.Transform(dst, src[:n], pSrc+n == len(s)) + pDst += nDst + pSrc += nSrc + + // TODO: let transformers implement an optional Spanner interface, akin + // to norm's QuickSpan. This would even allow us to avoid any allocation. + if !bytes.Equal(dst[:nDst], src[:nSrc]) { + break + } + pPrefix = pSrc + if err == ErrShortDst { + // A buffer can only be short if a transformer modifies its input. + break + } else if err == ErrShortSrc { + if nSrc == 0 { + // No progress was made. + break + } + // Equal so far and !atEOF, so continue checking. + } else if err != nil || pPrefix == len(s) { + return string(s[:pPrefix]), pPrefix, err + } + } + // Post-condition: pDst == pPrefix + nDst && pSrc == pPrefix + nSrc. + + // We have transformed the first pSrc bytes of the input s to become pDst + // transformed bytes. Those transformed bytes are discontiguous: the first + // pPrefix of them equal s[:pPrefix] and the last nDst of them equal + // dst[:nDst]. We copy them around, into a new dst buffer if necessary, so + // that they become one contiguous slice: dst[:pDst]. + if pPrefix != 0 { + newDst := dst + if pDst > len(newDst) { + newDst = make([]byte, len(s)+nDst-nSrc) + } + copy(newDst[pPrefix:pDst], dst[:nDst]) + copy(newDst[:pPrefix], s[:pPrefix]) + dst = newDst + } + + // Prevent duplicate Transform calls with atEOF being true at the end of + // the input. Also return if we have an unrecoverable error. + if (err == nil && pSrc == len(s)) || + (err != nil && err != ErrShortDst && err != ErrShortSrc) { + return string(dst[:pDst]), pSrc, err + } + + // Transform the remaining input, growing dst and src buffers as necessary. + for { + n := copy(src, s[pSrc:]) + atEOF := pSrc+n == len(s) + nDst, nSrc, err := t.Transform(dst[pDst:], src[:n], atEOF) + pDst += nDst + pSrc += nSrc + + // If we got ErrShortDst or ErrShortSrc, do not grow as long as we can + // make progress. This may avoid excessive allocations. + if err == ErrShortDst { + if nDst == 0 { + dst = grow(dst, pDst) + } + } else if err == ErrShortSrc { + if atEOF { + return string(dst[:pDst]), pSrc, err + } + if nSrc == 0 { + src = grow(src, 0) + } + } else if err != nil || pSrc == len(s) { + return string(dst[:pDst]), pSrc, err + } + } +} + +// Bytes returns a new byte slice with the result of converting b[:n] using t, +// where n <= len(b). If err == nil, n will be len(b). It calls Reset on t. +func Bytes(t Transformer, b []byte) (result []byte, n int, err error) { + return doAppend(t, 0, make([]byte, len(b)), b) +} + +// Append appends the result of converting src[:n] using t to dst, where +// n <= len(src), If err == nil, n will be len(src). It calls Reset on t. +func Append(t Transformer, dst, src []byte) (result []byte, n int, err error) { + if len(dst) == cap(dst) { + n := len(src) + len(dst) // It is okay for this to be 0. + b := make([]byte, n) + dst = b[:copy(b, dst)] + } + return doAppend(t, len(dst), dst[:cap(dst)], src) +} + +func doAppend(t Transformer, pDst int, dst, src []byte) (result []byte, n int, err error) { + t.Reset() + pSrc := 0 + for { + nDst, nSrc, err := t.Transform(dst[pDst:], src[pSrc:], true) + pDst += nDst + pSrc += nSrc + if err != ErrShortDst { + return dst[:pDst], pSrc, err + } + + // Grow the destination buffer, but do not grow as long as we can make + // progress. This may avoid excessive allocations. + if nDst == 0 { + dst = grow(dst, pDst) + } + } +} diff --git a/vendor/golang.org/x/text/unicode/cldr/base.go b/vendor/golang.org/x/text/unicode/cldr/base.go new file mode 100644 index 0000000..b71420c --- /dev/null +++ b/vendor/golang.org/x/text/unicode/cldr/base.go @@ -0,0 +1,105 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cldr + +import ( + "encoding/xml" + "regexp" + "strconv" +) + +// Elem is implemented by every XML element. +type Elem interface { + setEnclosing(Elem) + setName(string) + enclosing() Elem + + GetCommon() *Common +} + +type hidden struct { + CharData string `xml:",chardata"` + Alias *struct { + Common + Source string `xml:"source,attr"` + Path string `xml:"path,attr"` + } `xml:"alias"` + Def *struct { + Common + Choice string `xml:"choice,attr,omitempty"` + Type string `xml:"type,attr,omitempty"` + } `xml:"default"` +} + +// Common holds several of the most common attributes and sub elements +// of an XML element. +type Common struct { + XMLName xml.Name + name string + enclElem Elem + Type string `xml:"type,attr,omitempty"` + Reference string `xml:"reference,attr,omitempty"` + Alt string `xml:"alt,attr,omitempty"` + ValidSubLocales string `xml:"validSubLocales,attr,omitempty"` + Draft string `xml:"draft,attr,omitempty"` + hidden +} + +// Default returns the default type to select from the enclosed list +// or "" if no default value is specified. +func (e *Common) Default() string { + if e.Def == nil { + return "" + } + if e.Def.Choice != "" { + return e.Def.Choice + } else if e.Def.Type != "" { + // Type is still used by the default element in collation. + return e.Def.Type + } + return "" +} + +// Element returns the XML element name. +func (e *Common) Element() string { + return e.name +} + +// GetCommon returns e. It is provided such that Common implements Elem. +func (e *Common) GetCommon() *Common { + return e +} + +// Data returns the character data accumulated for this element. +func (e *Common) Data() string { + e.CharData = charRe.ReplaceAllStringFunc(e.CharData, replaceUnicode) + return e.CharData +} + +func (e *Common) setName(s string) { + e.name = s +} + +func (e *Common) enclosing() Elem { + return e.enclElem +} + +func (e *Common) setEnclosing(en Elem) { + e.enclElem = en +} + +// Escape characters that can be escaped without further escaping the string. +var charRe = regexp.MustCompile(`&#x[0-9a-fA-F]*;|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|\\x[0-9a-fA-F]{2}|\\[0-7]{3}|\\[abtnvfr]`) + +// replaceUnicode converts hexadecimal Unicode codepoint notations to a one-rune string. +// It assumes the input string is correctly formatted. +func replaceUnicode(s string) string { + if s[1] == '#' { + r, _ := strconv.ParseInt(s[3:len(s)-1], 16, 32) + return string(rune(r)) + } + r, _, _, _ := strconv.UnquoteChar(s, 0) + return string(r) +} diff --git a/vendor/golang.org/x/text/unicode/cldr/cldr.go b/vendor/golang.org/x/text/unicode/cldr/cldr.go new file mode 100644 index 0000000..f39b2e3 --- /dev/null +++ b/vendor/golang.org/x/text/unicode/cldr/cldr.go @@ -0,0 +1,137 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run makexml.go -output xml.go + +// Package cldr provides a parser for LDML and related XML formats. +// +// This package is intended to be used by the table generation tools for the +// various packages in x/text and is not internal for historical reasons. +// +// As the XML types are generated from the CLDR DTD, and as the CLDR standard is +// periodically amended, this package may change considerably over time. This +// mostly means that data may appear and disappear between versions. That is, +// old code should keep compiling for newer versions, but data may have moved or +// changed. CLDR version 22 is the first version supported by this package. +// Older versions may not work. +package cldr // import "golang.org/x/text/unicode/cldr" + +import ( + "fmt" + "sort" +) + +// CLDR provides access to parsed data of the Unicode Common Locale Data Repository. +type CLDR struct { + parent map[string][]string + locale map[string]*LDML + resolved map[string]*LDML + bcp47 *LDMLBCP47 + supp *SupplementalData +} + +func makeCLDR() *CLDR { + return &CLDR{ + parent: make(map[string][]string), + locale: make(map[string]*LDML), + resolved: make(map[string]*LDML), + bcp47: &LDMLBCP47{}, + supp: &SupplementalData{}, + } +} + +// BCP47 returns the parsed BCP47 LDML data. If no such data was parsed, nil is returned. +func (cldr *CLDR) BCP47() *LDMLBCP47 { + return nil +} + +// Draft indicates the draft level of an element. +type Draft int + +const ( + Approved Draft = iota + Contributed + Provisional + Unconfirmed +) + +var drafts = []string{"unconfirmed", "provisional", "contributed", "approved", ""} + +// ParseDraft returns the Draft value corresponding to the given string. The +// empty string corresponds to Approved. +func ParseDraft(level string) (Draft, error) { + if level == "" { + return Approved, nil + } + for i, s := range drafts { + if level == s { + return Unconfirmed - Draft(i), nil + } + } + return Approved, fmt.Errorf("cldr: unknown draft level %q", level) +} + +func (d Draft) String() string { + return drafts[len(drafts)-1-int(d)] +} + +// SetDraftLevel sets which draft levels to include in the evaluated LDML. +// Any draft element for which the draft level is higher than lev will be excluded. +// If multiple draft levels are available for a single element, the one with the +// lowest draft level will be selected, unless preferDraft is true, in which case +// the highest draft will be chosen. +// It is assumed that the underlying LDML is canonicalized. +func (cldr *CLDR) SetDraftLevel(lev Draft, preferDraft bool) { + // TODO: implement + cldr.resolved = make(map[string]*LDML) +} + +// RawLDML returns the LDML XML for id in unresolved form. +// id must be one of the strings returned by Locales. +func (cldr *CLDR) RawLDML(loc string) *LDML { + return cldr.locale[loc] +} + +// LDML returns the fully resolved LDML XML for loc, which must be one of +// the strings returned by Locales. +// +// Deprecated: Use RawLDML and implement inheritance manually or using the +// internal cldrtree package. +// Inheritance has changed quite a bit since the onset of this package and in +// practice data often represented in a way where knowledge of how it was +// inherited is relevant. +func (cldr *CLDR) LDML(loc string) (*LDML, error) { + return cldr.resolve(loc) +} + +// Supplemental returns the parsed supplemental data. If no such data was parsed, +// nil is returned. +func (cldr *CLDR) Supplemental() *SupplementalData { + return cldr.supp +} + +// Locales returns the locales for which there exist files. +// Valid sublocales for which there is no file are not included. +// The root locale is always sorted first. +func (cldr *CLDR) Locales() []string { + loc := []string{"root"} + hasRoot := false + for l, _ := range cldr.locale { + if l == "root" { + hasRoot = true + continue + } + loc = append(loc, l) + } + sort.Strings(loc[1:]) + if !hasRoot { + return loc[1:] + } + return loc +} + +// Get fills in the fields of x based on the XPath path. +func Get(e Elem, path string) (res Elem, err error) { + return walkXPath(e, path) +} diff --git a/vendor/golang.org/x/text/unicode/cldr/collate.go b/vendor/golang.org/x/text/unicode/cldr/collate.go new file mode 100644 index 0000000..27c5bac --- /dev/null +++ b/vendor/golang.org/x/text/unicode/cldr/collate.go @@ -0,0 +1,359 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cldr + +import ( + "bufio" + "encoding/xml" + "errors" + "fmt" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +// RuleProcessor can be passed to Collator's Process method, which +// parses the rules and calls the respective method for each rule found. +type RuleProcessor interface { + Reset(anchor string, before int) error + Insert(level int, str, context, extend string) error + Index(id string) +} + +const ( + // cldrIndex is a Unicode-reserved sentinel value used to mark the start + // of a grouping within an index. + // We ignore any rule that starts with this rune. + // See https://unicode.org/reports/tr35/#Collation_Elements for details. + cldrIndex = "\uFDD0" + + // specialAnchor is the format in which to represent logical reset positions, + // such as "first tertiary ignorable". + specialAnchor = "<%s/>" +) + +// Process parses the rules for the tailorings of this collation +// and calls the respective methods of p for each rule found. +func (c Collation) Process(p RuleProcessor) (err error) { + if len(c.Cr) > 0 { + if len(c.Cr) > 1 { + return fmt.Errorf("multiple cr elements, want 0 or 1") + } + return processRules(p, c.Cr[0].Data()) + } + if c.Rules.Any != nil { + return c.processXML(p) + } + return errors.New("no tailoring data") +} + +// processRules parses rules in the Collation Rule Syntax defined in +// https://www.unicode.org/reports/tr35/tr35-collation.html#Collation_Tailorings. +func processRules(p RuleProcessor, s string) (err error) { + chk := func(s string, e error) string { + if err == nil { + err = e + } + return s + } + i := 0 // Save the line number for use after the loop. + scanner := bufio.NewScanner(strings.NewReader(s)) + for ; scanner.Scan() && err == nil; i++ { + for s := skipSpace(scanner.Text()); s != "" && s[0] != '#'; s = skipSpace(s) { + level := 5 + var ch byte + switch ch, s = s[0], s[1:]; ch { + case '&': // followed by or '[' ']' + if s = skipSpace(s); consume(&s, '[') { + s = chk(parseSpecialAnchor(p, s)) + } else { + s = chk(parseAnchor(p, 0, s)) + } + case '<': // sort relation '<'{1,4}, optionally followed by '*'. + for level = 1; consume(&s, '<'); level++ { + } + if level > 4 { + err = fmt.Errorf("level %d > 4", level) + } + fallthrough + case '=': // identity relation, optionally followed by *. + if consume(&s, '*') { + s = chk(parseSequence(p, level, s)) + } else { + s = chk(parseOrder(p, level, s)) + } + default: + chk("", fmt.Errorf("illegal operator %q", ch)) + break + } + } + } + if chk("", scanner.Err()); err != nil { + return fmt.Errorf("%d: %v", i, err) + } + return nil +} + +// parseSpecialAnchor parses the anchor syntax which is either of the form +// ['before' ] +// or +// [