├── commands ├── testdata │ ├── a.txt │ ├── b.txt │ └── aa.txt ├── config_test.go ├── download_test.go ├── upload_test.go ├── download.go ├── config.go └── upload.go ├── Godeps ├── _workspace │ ├── .gitignore │ └── src │ │ ├── github.com │ │ └── codegangsta │ │ │ └── cli │ │ │ ├── autocomplete │ │ │ ├── zsh_autocomplete │ │ │ └── bash_autocomplete │ │ │ ├── .travis.yml │ │ │ ├── cli.go │ │ │ ├── LICENSE │ │ │ ├── command.go │ │ │ ├── help.go │ │ │ ├── app.go │ │ │ ├── README.md │ │ │ └── context.go │ │ └── golang.org │ │ └── x │ │ └── crypto │ │ ├── ssh │ │ ├── test │ │ │ └── doc.go │ │ ├── terminal │ │ │ ├── util_bsd.go │ │ │ ├── util_linux.go │ │ │ ├── util.go │ │ │ └── util_windows.go │ │ ├── doc.go │ │ ├── mac.go │ │ ├── buffer.go │ │ ├── agent │ │ │ ├── forward.go │ │ │ ├── keyring.go │ │ │ └── server.go │ │ ├── connection.go │ │ ├── client.go │ │ ├── mux.go │ │ ├── transport.go │ │ └── common.go │ │ └── curve25519 │ │ ├── const_amd64.s │ │ ├── doc.go │ │ ├── cswap_amd64.s │ │ ├── freeze_amd64.s │ │ ├── square_amd64.s │ │ ├── mul_amd64.s │ │ └── mont25519_amd64.go ├── Readme └── Godeps.json ├── prepare_bintray_deployment.sh ├── .gitignore ├── tests └── utils.go ├── bintray_descriptors ├── linux_386.json ├── linux_amd64.json ├── darwin_amd64.json └── windows_amd64.json ├── utils ├── utils.go ├── sshLogin.go ├── artifactoryUtils.go ├── aqlQueryBuilder.go └── ioUtils.go ├── .travis.yml ├── README.md ├── LICENSE └── art └── main.go /commands/testdata/a.txt: -------------------------------------------------------------------------------- 1 | aaaaaaaaaa -------------------------------------------------------------------------------- /commands/testdata/b.txt: -------------------------------------------------------------------------------- 1 | aaaaaaaaaa -------------------------------------------------------------------------------- /commands/testdata/aa.txt: -------------------------------------------------------------------------------- 1 | aaaaaaaaaa -------------------------------------------------------------------------------- /Godeps/_workspace/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg 2 | /bin 3 | -------------------------------------------------------------------------------- /prepare_bintray_deployment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete: -------------------------------------------------------------------------------- 1 | autoload -U compinit && compinit 2 | autoload -U bashcompinit && bashcompinit 3 | 4 | script_dir=$(dirname $0) 5 | source ${script_dir}/bash_autocomplete 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | 4 | go: 5 | - 1.0.3 6 | - 1.1.2 7 | - 1.2.2 8 | - 1.3.3 9 | - 1.4.2 10 | - 1.5.1 11 | - tip 12 | 13 | matrix: 14 | allow_failures: 15 | - go: tip 16 | 17 | script: 18 | - go vet ./... 19 | - go test -v ./... 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/test/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This package contains integration tests for the 6 | // golang.org/x/crypto/ssh package. 7 | package test 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | idea -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/terminal/util_bsd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin dragonfly freebsd netbsd openbsd 6 | 7 | package terminal 8 | 9 | import "syscall" 10 | 11 | const ioctlReadTermios = syscall.TIOCGETA 12 | const ioctlWriteTermios = syscall.TIOCSETA 13 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | : ${PROG:=$(basename ${BASH_SOURCE})} 4 | 5 | _cli_bash_autocomplete() { 6 | local cur opts base 7 | COMPREPLY=() 8 | cur="${COMP_WORDS[COMP_CWORD]}" 9 | opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) 10 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 11 | return 0 12 | } 13 | 14 | complete -F _cli_bash_autocomplete $PROG 15 | -------------------------------------------------------------------------------- /tests/utils.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "runtime" 5 | "github.com/JFrogDev/artifactory-cli-go/utils" 6 | ) 7 | 8 | func GetFlags() *utils.Flags { 9 | flags := new(utils.Flags) 10 | flags.ArtDetails = new(utils.ArtifactoryDetails) 11 | flags.DryRun = true 12 | flags.EncPassword = true 13 | flags.Threads = 3 14 | 15 | return flags 16 | } 17 | 18 | func GetFileSeperator() string { 19 | if runtime.GOOS == "windows" { 20 | return "\\\\" 21 | } 22 | return "/" 23 | } -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/terminal/util_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package terminal 6 | 7 | // These constants are declared here, rather than importing 8 | // them from the syscall package as some syscall packages, even 9 | // on linux, for example gccgo, do not declare them. 10 | const ioctlReadTermios = 0x5401 // syscall.TCGETS 11 | const ioctlWriteTermios = 0x5402 // syscall.TCSETS 12 | -------------------------------------------------------------------------------- /bintray_descriptors/linux_386.json: -------------------------------------------------------------------------------- 1 | { 2 | "package": { 3 | "name": "artifactory-cli-linux-386", 4 | "repo": "artifactory-cli-go", 5 | "licenses": ["Apache-2.0"], 6 | "vcs_url": "https://github.com/JFrogDev/artifactory-cli-go.git", 7 | "subject": "jfrog" 8 | }, 9 | "version": { 10 | "name": "1.3.0", 11 | "vcs_tag": "1.3.0", 12 | "gpgSign": true 13 | }, 14 | "files": 15 | [{"includePattern": "../../../../bin/art", "uploadPattern": "1.3.0/artifactory-cli-linux-386/art"}], 16 | "publish": false 17 | } 18 | -------------------------------------------------------------------------------- /bintray_descriptors/linux_amd64.json: -------------------------------------------------------------------------------- 1 | { 2 | "package": { 3 | "name": "artifactory-cli-linux-amd64", 4 | "repo": "artifactory-cli-go", 5 | "licenses": ["Apache-2.0"], 6 | "vcs_url": "https://github.com/JFrogDev/artifactory-cli-go.git", 7 | "subject": "jfrog" 8 | }, 9 | "version": { 10 | "name": "1.3.0", 11 | "vcs_tag": "1.3.0", 12 | "gpgSign": true 13 | }, 14 | "files": 15 | [{"includePattern": "../../../../bin/art", "uploadPattern": "1.3.0/artifactory-cli-linux-amd64/art"}], 16 | "publish": false 17 | } 18 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/JFrogDev/artifactory-cli-go", 3 | "GoVersion": "go1.5.2", 4 | "Packages": [ 5 | ".\\art" 6 | ], 7 | "Deps": [ 8 | { 9 | "ImportPath": "github.com/codegangsta/cli", 10 | "Comment": "1.2.0-187-gc31a797", 11 | "Rev": "c31a7975863e7810c92e2e288a9ab074f9a88f29" 12 | }, 13 | { 14 | "ImportPath": "golang.org/x/crypto/curve25519", 15 | "Rev": "f18420efc3b4f8e9f3d51f6bd2476e92c46260e9" 16 | }, 17 | { 18 | "ImportPath": "golang.org/x/crypto/ssh", 19 | "Rev": "f18420efc3b4f8e9f3d51f6bd2476e92c46260e9" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /bintray_descriptors/darwin_amd64.json: -------------------------------------------------------------------------------- 1 | { 2 | "package": { 3 | "name": "artifactory-cli-mac-386", 4 | "repo": "artifactory-cli-go", 5 | "licenses": ["Apache-2.0"], 6 | "vcs_url": "https://github.com/JFrogDev/artifactory-cli-go.git", 7 | "subject": "jfrog" 8 | }, 9 | "version": { 10 | "name": "1.3.0", 11 | "vcs_tag": "1.3.0", 12 | "gpgSign": true 13 | }, 14 | "files": 15 | [{"includePattern": "../../../../bin/darwin_amd64/art", "uploadPattern": "1.3.0/artifactory-cli-mac-386/art"}], 16 | "publish": false 17 | } 18 | -------------------------------------------------------------------------------- /bintray_descriptors/windows_amd64.json: -------------------------------------------------------------------------------- 1 | { 2 | "package": { 3 | "name": "artifactory-cli-windows-amd64", 4 | "repo": "artifactory-cli-go", 5 | "licenses": ["Apache-2.0"], 6 | "vcs_url": "https://github.com/JFrogDev/artifactory-cli-go.git", 7 | "subject": "jfrog" 8 | }, 9 | "version": { 10 | "name": "1.3.0", 11 | "vcs_tag": "1.3.0", 12 | "gpgSign": true 13 | }, 14 | "files": 15 | [{"includePattern": "../../../../bin/windows_amd64/art.exe", "uploadPattern": "1.3.0/windows_amd64/art.exe"}], 16 | "publish": false 17 | } 18 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/curve25519/const_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This code was translated into a form compatible with 6a from the public 6 | // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html 7 | 8 | // +build amd64,!gccgo,!appengine 9 | 10 | DATA ·REDMASK51(SB)/8, $0x0007FFFFFFFFFFFF 11 | GLOBL ·REDMASK51(SB), 8, $8 12 | 13 | DATA ·_121666_213(SB)/8, $996687872 14 | GLOBL ·_121666_213(SB), 8, $8 15 | 16 | DATA ·_2P0(SB)/8, $0xFFFFFFFFFFFDA 17 | GLOBL ·_2P0(SB), 8, $8 18 | 19 | DATA ·_2P1234(SB)/8, $0xFFFFFFFFFFFFE 20 | GLOBL ·_2P1234(SB), 8, $8 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package ssh implements an SSH client and server. 7 | 8 | SSH is a transport security protocol, an authentication protocol and a 9 | family of application protocols. The most typical application level 10 | protocol is a remote shell and this is specifically implemented. However, 11 | the multiplexed nature of SSH is exposed to users that wish to support 12 | others. 13 | 14 | References: 15 | [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD 16 | [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 17 | */ 18 | package ssh 19 | -------------------------------------------------------------------------------- /commands/config_test.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "reflect" 7 | "encoding/json" 8 | "github.com/JFrogDev/artifactory-cli-go/utils" 9 | ) 10 | 11 | func TestConfig(t *testing.T){ 12 | inputDetails := utils.ArtifactoryDetails { "http://localhost:8080/artifactory", "admin", "password", "", nil } 13 | Config(&inputDetails, false, false) 14 | outputConfig := GetConfig() 15 | printConfigStruct(&inputDetails) 16 | printConfigStruct(outputConfig) 17 | if !reflect.DeepEqual(inputDetails, *outputConfig) { 18 | t.Error("Unexpected configuration was saved to file. Expected: " + configStructToString(&inputDetails) + " Got " + configStructToString(outputConfig)) 19 | } 20 | } 21 | 22 | func configStructToString(artConfig *utils.ArtifactoryDetails) string { 23 | marshaledStruct, _ := json.Marshal(*artConfig) 24 | return string(marshaledStruct) 25 | } 26 | 27 | func printConfigStruct(artConfig *utils.ArtifactoryDetails){ 28 | stringSturct := configStructToString(artConfig) 29 | fmt.Println(stringSturct) 30 | } -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/curve25519/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package curve25519 provides an implementation of scalar multiplication on 6 | // the elliptic curve known as curve25519. See http://cr.yp.to/ecdh.html 7 | package curve25519 8 | 9 | // basePoint is the x coordinate of the generator of the curve. 10 | var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 11 | 12 | // ScalarMult sets dst to the product in*base where dst and base are the x 13 | // coordinates of group points and all values are in little-endian form. 14 | func ScalarMult(dst, in, base *[32]byte) { 15 | scalarMult(dst, in, base) 16 | } 17 | 18 | // ScalarBaseMult sets dst to the product in*base where dst and base are the x 19 | // coordinates of group points, base is the standard generator and all values 20 | // are in little-endian form. 21 | func ScalarBaseMult(dst, in *[32]byte) { 22 | ScalarMult(dst, in, &basePoint) 23 | } 24 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/cli.go: -------------------------------------------------------------------------------- 1 | // Package cli provides a minimal framework for creating and organizing command line 2 | // Go applications. cli is designed to be easy to understand and write, the most simple 3 | // cli application can be written as follows: 4 | // func main() { 5 | // cli.NewApp().Run(os.Args) 6 | // } 7 | // 8 | // Of course this application does not do much, so let's make this an actual application: 9 | // func main() { 10 | // app := cli.NewApp() 11 | // app.Name = "greet" 12 | // app.Usage = "say a greeting" 13 | // app.Action = func(c *cli.Context) { 14 | // println("Greetings") 15 | // } 16 | // 17 | // app.Run(os.Args) 18 | // } 19 | package cli 20 | 21 | import ( 22 | "strings" 23 | ) 24 | 25 | type MultiError struct { 26 | Errors []error 27 | } 28 | 29 | func NewMultiError(err ...error) MultiError { 30 | return MultiError{Errors: err} 31 | } 32 | 33 | func (m MultiError) Error() string { 34 | errs := make([]string, len(m.Errors)) 35 | for i, err := range m.Errors { 36 | errs[i] = err.Error() 37 | } 38 | 39 | return strings.Join(errs, "\n") 40 | } 41 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 Jeremy Saenz 2 | All Rights Reserved. 3 | 4 | MIT LICENSE 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/mac.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ssh 6 | 7 | // Message authentication support 8 | 9 | import ( 10 | "crypto/hmac" 11 | "crypto/sha1" 12 | "crypto/sha256" 13 | "hash" 14 | ) 15 | 16 | type macMode struct { 17 | keySize int 18 | new func(key []byte) hash.Hash 19 | } 20 | 21 | // truncatingMAC wraps around a hash.Hash and truncates the output digest to 22 | // a given size. 23 | type truncatingMAC struct { 24 | length int 25 | hmac hash.Hash 26 | } 27 | 28 | func (t truncatingMAC) Write(data []byte) (int, error) { 29 | return t.hmac.Write(data) 30 | } 31 | 32 | func (t truncatingMAC) Sum(in []byte) []byte { 33 | out := t.hmac.Sum(in) 34 | return out[:len(in)+t.length] 35 | } 36 | 37 | func (t truncatingMAC) Reset() { 38 | t.hmac.Reset() 39 | } 40 | 41 | func (t truncatingMAC) Size() int { 42 | return t.length 43 | } 44 | 45 | func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } 46 | 47 | var macModes = map[string]*macMode{ 48 | "hmac-sha2-256": {32, func(key []byte) hash.Hash { 49 | return hmac.New(sha256.New, key) 50 | }}, 51 | "hmac-sha1": {20, func(key []byte) hash.Hash { 52 | return hmac.New(sha1.New, key) 53 | }}, 54 | "hmac-sha1-96": {20, func(key []byte) hash.Hash { 55 | return truncatingMAC{12, hmac.New(sha1.New, key)} 56 | }}, 57 | } 58 | -------------------------------------------------------------------------------- /commands/download_test.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "testing" 5 | "github.com/JFrogDev/artifactory-cli-go/tests" 6 | ) 7 | 8 | func TestRecursiveDownload(t *testing.T) { 9 | flags := tests.GetFlags() 10 | flags.Recursive = true 11 | aqlResult := Download("repo-local", flags) 12 | expected := "items.find({\"repo\": \"repo-local\",\"$or\": [{\"$and\": [{\"path\": {\"$match\":\"*\"},\"name\":{\"$match\":\"*\"}}]}]})" 13 | 14 | if aqlResult != expected { 15 | t.Error("Unexpected download AQL query built. Expected: " + expected + " Got " + aqlResult) 16 | } 17 | 18 | aqlResult = Download("repo-local2/a*b*c/dd/", flags) 19 | expected = "items.find({\"repo\": \"repo-local2\",\"$or\": [{\"$and\": [{\"path\": {\"$match\":\"a*b*c/dd\"},\"name\":{\"$match\":\"*\"}}]},{\"$and\": [{\"path\": {\"$match\":\"a*b*c/dd/*\"},\"name\":{\"$match\":\"*\"}}]}]})" 20 | 21 | if aqlResult != expected { 22 | t.Error("Unexpected download AQL query built. Expected: " + expected + " Got " + aqlResult) 23 | } 24 | } 25 | 26 | func TestNonRecursiveDownload(t *testing.T) { 27 | flags := tests.GetFlags() 28 | flags.Recursive = false 29 | aqlResult := Download("repo-local", flags) 30 | expected := "items.find({\"repo\": \"repo-local\",\"$or\": [{\"$and\": [{\"path\": {\"$match\":\".\"},\"name\":{\"$match\":\"*\"}}]}]})" 31 | 32 | if aqlResult != expected { 33 | t.Error("Unexpected download AQL query built. Expected: " + expected + " Got " + aqlResult) 34 | } 35 | 36 | aqlResult = Download("repo-local2/a*b*c/dd/", flags) 37 | expected = "items.find({\"repo\": \"repo-local2\",\"$or\": [{\"$and\": [{\"path\": {\"$match\":\"a*b*c/dd\"},\"name\":{\"$match\":\"*\"}}]}]})" 38 | 39 | if aqlResult != expected { 40 | t.Error("Unexpected download AQL query built. Expected: " + expected + " Got " + aqlResult) 41 | } 42 | } -------------------------------------------------------------------------------- /commands/upload_test.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "testing" 5 | "strconv" 6 | "github.com/JFrogDev/artifactory-cli-go/utils" 7 | "github.com/JFrogDev/artifactory-cli-go/tests" 8 | ) 9 | 10 | func TestSingleFileUpload(t *testing.T) { 11 | flags := tests.GetFlags() 12 | uploaded1, _ := Upload("testdata/a.txt", "repo-local", flags) 13 | uploaded2, _ := Upload("testdata/aa.txt", "repo-local", flags) 14 | uploaded3, _ := Upload("testdata/aa1*.txt", "repo-local", flags) 15 | if uploaded1 != 1 { 16 | t.Error("Expected 1 file to be uploaded. Got " + strconv.Itoa(uploaded1) + ".") 17 | } 18 | if uploaded2 != 1 { 19 | t.Error("Expected 1 file to be uploaded. Got " + strconv.Itoa(uploaded2) + ".") 20 | } 21 | if uploaded3 != 0 { 22 | t.Error("Expected 0 file to be uploaded. Got " + strconv.Itoa(uploaded3) + ".") 23 | } 24 | } 25 | 26 | func TestPatternRecursiveUpload(t *testing.T) { 27 | flags := tests.GetFlags() 28 | flags.Recursive = true 29 | testPatternUpload(t, flags) 30 | } 31 | 32 | func TestPatternNonRecursiveUpload(t *testing.T) { 33 | flags := tests.GetFlags() 34 | flags.Recursive = false 35 | testPatternUpload(t, flags) 36 | } 37 | 38 | func testPatternUpload(t *testing.T, flags *utils.Flags) { 39 | sep := tests.GetFileSeperator() 40 | uploaded1, _ := Upload("testdata" + sep + "*", "repo-local", flags) 41 | uploaded2, _ := Upload("testdata" + sep + "a*", "repo-local", flags) 42 | uploaded3, _ := Upload("testdata" + sep + "b*", "repo-local", flags) 43 | 44 | if uploaded1 != 3 { 45 | t.Error("Expected 3 file to be uploaded. Got " + strconv.Itoa(uploaded1) + ".") 46 | } 47 | if uploaded2 != 2 { 48 | t.Error("Expected 2 file to be uploaded. Got " + strconv.Itoa(uploaded2) + ".") 49 | } 50 | if uploaded3 != 1 { 51 | t.Error("Expected 1 file to be uploaded. Got " + strconv.Itoa(uploaded3) + ".") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "os" 5 | "fmt" 6 | "strings" 7 | "os/user" 8 | "strconv" 9 | "runtime" 10 | ) 11 | 12 | var ExitCodeError ExitCode = ExitCode{1} 13 | var ExitCodeWarning ExitCode = ExitCode{2} 14 | 15 | type ExitCode struct { 16 | Code int 17 | } 18 | 19 | func GetVersion() string { 20 | return "1.3.0" 21 | } 22 | 23 | func CheckError(err error) { 24 | if err != nil { 25 | panic(err) 26 | } 27 | } 28 | 29 | func Exit(exitCode ExitCode, msg string) { 30 | if msg != "" { 31 | fmt.Println(msg) 32 | } 33 | os.Exit(exitCode.Code) 34 | } 35 | 36 | func GetLogMsgPrefix(threadId int, dryRun bool) string { 37 | var strDryRun string 38 | if dryRun { 39 | strDryRun = " [Dry run]" 40 | } else { 41 | strDryRun = "" 42 | } 43 | return "[Thread " + strconv.Itoa(threadId) + "]" + strDryRun 44 | } 45 | 46 | func GetFileSeperator() string { 47 | if runtime.GOOS == "windows" { 48 | return "\\" 49 | } 50 | return "/" 51 | } 52 | 53 | func GetHomeDir() string { 54 | user, err := user.Current() 55 | if err == nil { 56 | return user.HomeDir 57 | } 58 | home := os.Getenv("HOME") 59 | if home != "" { 60 | return home 61 | } 62 | return ""; 63 | } 64 | 65 | func AddTrailingSlashIfNeeded(url string) string { 66 | if url != "" && !strings.HasSuffix(url, "/") { 67 | url += "/" 68 | } 69 | return url 70 | } 71 | 72 | type Flags struct { 73 | ArtDetails *ArtifactoryDetails 74 | DryRun bool 75 | Props string 76 | Deb string 77 | Recursive bool 78 | Flat bool 79 | UseRegExp bool 80 | Threads int 81 | MinSplitSize int64 82 | SplitCount int 83 | Interactive bool 84 | EncPassword bool 85 | } 86 | 87 | type ArtifactoryDetails struct { 88 | Url string 89 | User string 90 | Password string 91 | SshKeyPath string 92 | SshAuthHeaders map[string]string 93 | } -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/curve25519/cswap_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This code was translated into a form compatible with 6a from the public 6 | // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html 7 | 8 | // +build amd64,!gccgo,!appengine 9 | 10 | // func cswap(inout *[5]uint64, v uint64) 11 | TEXT ·cswap(SB),7,$0 12 | MOVQ inout+0(FP),DI 13 | MOVQ v+8(FP),SI 14 | 15 | CMPQ SI,$1 16 | MOVQ 0(DI),SI 17 | MOVQ 80(DI),DX 18 | MOVQ 8(DI),CX 19 | MOVQ 88(DI),R8 20 | MOVQ SI,R9 21 | CMOVQEQ DX,SI 22 | CMOVQEQ R9,DX 23 | MOVQ CX,R9 24 | CMOVQEQ R8,CX 25 | CMOVQEQ R9,R8 26 | MOVQ SI,0(DI) 27 | MOVQ DX,80(DI) 28 | MOVQ CX,8(DI) 29 | MOVQ R8,88(DI) 30 | MOVQ 16(DI),SI 31 | MOVQ 96(DI),DX 32 | MOVQ 24(DI),CX 33 | MOVQ 104(DI),R8 34 | MOVQ SI,R9 35 | CMOVQEQ DX,SI 36 | CMOVQEQ R9,DX 37 | MOVQ CX,R9 38 | CMOVQEQ R8,CX 39 | CMOVQEQ R9,R8 40 | MOVQ SI,16(DI) 41 | MOVQ DX,96(DI) 42 | MOVQ CX,24(DI) 43 | MOVQ R8,104(DI) 44 | MOVQ 32(DI),SI 45 | MOVQ 112(DI),DX 46 | MOVQ 40(DI),CX 47 | MOVQ 120(DI),R8 48 | MOVQ SI,R9 49 | CMOVQEQ DX,SI 50 | CMOVQEQ R9,DX 51 | MOVQ CX,R9 52 | CMOVQEQ R8,CX 53 | CMOVQEQ R9,R8 54 | MOVQ SI,32(DI) 55 | MOVQ DX,112(DI) 56 | MOVQ CX,40(DI) 57 | MOVQ R8,120(DI) 58 | MOVQ 48(DI),SI 59 | MOVQ 128(DI),DX 60 | MOVQ 56(DI),CX 61 | MOVQ 136(DI),R8 62 | MOVQ SI,R9 63 | CMOVQEQ DX,SI 64 | CMOVQEQ R9,DX 65 | MOVQ CX,R9 66 | CMOVQEQ R8,CX 67 | CMOVQEQ R9,R8 68 | MOVQ SI,48(DI) 69 | MOVQ DX,128(DI) 70 | MOVQ CX,56(DI) 71 | MOVQ R8,136(DI) 72 | MOVQ 64(DI),SI 73 | MOVQ 144(DI),DX 74 | MOVQ 72(DI),CX 75 | MOVQ 152(DI),R8 76 | MOVQ SI,R9 77 | CMOVQEQ DX,SI 78 | CMOVQEQ R9,DX 79 | MOVQ CX,R9 80 | CMOVQEQ R8,CX 81 | CMOVQEQ R9,R8 82 | MOVQ SI,64(DI) 83 | MOVQ DX,144(DI) 84 | MOVQ CX,72(DI) 85 | MOVQ R8,152(DI) 86 | MOVQ DI,AX 87 | MOVQ SI,DX 88 | RET 89 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/curve25519/freeze_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This code was translated into a form compatible with 6a from the public 6 | // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html 7 | 8 | // +build amd64,!gccgo,!appengine 9 | 10 | // func freeze(inout *[5]uint64) 11 | TEXT ·freeze(SB),7,$96-8 12 | MOVQ inout+0(FP), DI 13 | 14 | MOVQ SP,R11 15 | MOVQ $31,CX 16 | NOTQ CX 17 | ANDQ CX,SP 18 | ADDQ $32,SP 19 | 20 | MOVQ R11,0(SP) 21 | MOVQ R12,8(SP) 22 | MOVQ R13,16(SP) 23 | MOVQ R14,24(SP) 24 | MOVQ R15,32(SP) 25 | MOVQ BX,40(SP) 26 | MOVQ BP,48(SP) 27 | MOVQ 0(DI),SI 28 | MOVQ 8(DI),DX 29 | MOVQ 16(DI),CX 30 | MOVQ 24(DI),R8 31 | MOVQ 32(DI),R9 32 | MOVQ ·REDMASK51(SB),AX 33 | MOVQ AX,R10 34 | SUBQ $18,R10 35 | MOVQ $3,R11 36 | REDUCELOOP: 37 | MOVQ SI,R12 38 | SHRQ $51,R12 39 | ANDQ AX,SI 40 | ADDQ R12,DX 41 | MOVQ DX,R12 42 | SHRQ $51,R12 43 | ANDQ AX,DX 44 | ADDQ R12,CX 45 | MOVQ CX,R12 46 | SHRQ $51,R12 47 | ANDQ AX,CX 48 | ADDQ R12,R8 49 | MOVQ R8,R12 50 | SHRQ $51,R12 51 | ANDQ AX,R8 52 | ADDQ R12,R9 53 | MOVQ R9,R12 54 | SHRQ $51,R12 55 | ANDQ AX,R9 56 | IMUL3Q $19,R12,R12 57 | ADDQ R12,SI 58 | SUBQ $1,R11 59 | JA REDUCELOOP 60 | MOVQ $1,R12 61 | CMPQ R10,SI 62 | CMOVQLT R11,R12 63 | CMPQ AX,DX 64 | CMOVQNE R11,R12 65 | CMPQ AX,CX 66 | CMOVQNE R11,R12 67 | CMPQ AX,R8 68 | CMOVQNE R11,R12 69 | CMPQ AX,R9 70 | CMOVQNE R11,R12 71 | NEGQ R12 72 | ANDQ R12,AX 73 | ANDQ R12,R10 74 | SUBQ R10,SI 75 | SUBQ AX,DX 76 | SUBQ AX,CX 77 | SUBQ AX,R8 78 | SUBQ AX,R9 79 | MOVQ SI,0(DI) 80 | MOVQ DX,8(DI) 81 | MOVQ CX,16(DI) 82 | MOVQ R8,24(DI) 83 | MOVQ R9,32(DI) 84 | MOVQ 0(SP),R11 85 | MOVQ 8(SP),R12 86 | MOVQ 16(SP),R13 87 | MOVQ 24(SP),R14 88 | MOVQ 32(SP),R15 89 | MOVQ 40(SP),BX 90 | MOVQ 48(SP),BP 91 | MOVQ R11,SP 92 | MOVQ DI,AX 93 | MOVQ SI,DX 94 | RET 95 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: required 3 | go: 4 | - 1.5.2 5 | 6 | env: 7 | matrix: 8 | - GIMME_OS=windows GIMME_ARCH=amd64 9 | - GIMME_OS=linux GIMME_ARCH=amd64 10 | - GIMME_OS=linux GIMME_ARCH=386 11 | - GIMME_OS=darwin GIMME_ARCH=amd64 12 | global: 13 | - secure: c/hl3zSgofRYSgIAgpStLEDx8szp1s1P9Gh3+fKNL9B/134j5B48arOx/bzWquUSKnvfwYddK4mlMDNDJBt0E2nul0nILTO1DTG7MTVExXKapVtdCj/9+P4OWLJTi8rDbC8d3N+t6E/mpczwnFVA3CPraDrEXWFmmRtm62eZLbMAvpESVdWF4xu8+9cDdv507H1re0d+IPFH9PK+B7aokyAjFVzf2719TYV7o9v3NakkLIWlnR18PnB6RX7vHBV7ziGWBOWHhpJIpne8StlBNTPwlbiGb80lNdUT2BLr9o66VuOT11L3K4QromWroDwiWc6MCA0Lvs6r8+I3PHebvXVjVEuzeSwJF9Szhe+UDL2FhFAdjecJ94tmfOG6X/P83Gl3aH3JnRHNgF7SnyO+WCB214w2zWz7EFCRJ9ji2SiqbmUFrbhxkGKF1RkYQP/r7NSq7QR20Esyp9vZurwLD7Qm7WnhkS0MFfBNKAFX/mPdsoajrqNzfUDR6/VYsiCQNreuTOawQamxYQ+IjcvG+3UIiEXPWuifd8idB2lprUwBlwHy0joUDNUwu6+Jb7xcUCbqK9w/ZYShUfpfSUKn5LMquGJpsTSeDXqfBJWQqR5e1QToteFu8UwyRuLUg2qvUwVS81T3NPy0Bg+L2v+TnInOnDWc8oIg2xJM5WaZATU= 14 | - secure: agExFpqoamoCHJY/vC66FwGJA2B6HXkW5cdgX2+CraeO4g1SVRJiX3296l/msFYbCfnTn7GuBnkpyhrbZ6NzndHGgXCjD0Q5RG85QF2ylVVr7/rlf/GC1l+IgxR7XmbXBv0DgfzFkrTbKqDJUJ6e5jpj29RKRHKEuX0f+OvycNL4b/tJIM4fyUcPq5SUTfYjAFwxh/00zkNhLjw3o24N3kK/JU8hipFbyGOf0pGAQg6rpZ3oDgHD24AoLRXCLFrjs+V07AXxMRqN+sCxYRxiQs8hYdCRgTVjyCTiAqDiV5Xu6lUtLzbVHWORnOP9+8GDnrI85Gv1lPpfn6nQNF+fxmcYBWq/RhebR7Mgc8v94yWQacBOuyxpgNY/FPO2XXXTL5y3PRxndsPoe8L7k3b9b2kx4H4/elkIRM/KMk1DdM7E1x4q3EhQUTKA7E6ZzWficuYL8OLHbLcKeASK+WDoJgWbXTzbVx4GgiKpp2gFQ19Kfs84t0lCU44OVg1OmksOS7llff47lgMJL5NPlIV77Dddmlud+9aPi0O4NUq8B8o2mpt8C13re0z8HNL5ce2fmHGi/XXnMMdSkTv6oC/jwqx4fo+6AxvVSxb6Dqh0U3gqmnlZngbz5UZCugp3GCSbNfvcMoyzWWx2AQTW6Y/Kpx+PWDLULV/19BTGez6L7m4= 15 | 16 | after_script: 17 | - ls -l ../../../../bin 18 | 19 | # Workaround for travis-ci/gimme#25 to pick up latest version of gimme and run again 20 | before_install: 21 | - curl -o gimme -sL https://raw.githubusercontent.com/travis-ci/gimme/master/gimme 22 | - chmod u+x gimme 23 | - unset GOROOT GOTOOLDIR 24 | - eval "$(./gimme 1.5)" 25 | - go version 26 | - go env 27 | 28 | # Part of the same workaround. 29 | before_script: 30 | - go get -d -v ./... 31 | 32 | # Part of the same workaround. 33 | script: 34 | - go build -v ./... 35 | 36 | deploy: 37 | skip_cleanup: true 38 | file: bintray_descriptors/${GIMME_OS}_${GIMME_ARCH}.json 39 | provider: bintray 40 | key: "$API_KEY" 41 | passphrase: "$PASSPHRASE" 42 | user: jfrogdev -------------------------------------------------------------------------------- /utils/sshLogin.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "io" 5 | "fmt" 6 | "bytes" 7 | "regexp" 8 | "strconv" 9 | "io/ioutil" 10 | "encoding/json" 11 | "github.com/JFrogDev/artifactory-cli-go/Godeps/_workspace/src/golang.org/x/crypto/ssh" 12 | ) 13 | 14 | func SshAuthentication(details *ArtifactoryDetails) { 15 | _, host, port := parseUrl(details.Url) 16 | 17 | fmt.Println("Performing SSH authentication...") 18 | if details.SshKeyPath == "" { 19 | Exit(ExitCodeError, "Cannot invoke the SshAuthentication function with no SSH key path. ") 20 | } 21 | 22 | buffer, err := ioutil.ReadFile(details.SshKeyPath) 23 | CheckError(err) 24 | key, err := ssh.ParsePrivateKey(buffer) 25 | CheckError(err) 26 | sshConfig := &ssh.ClientConfig{ 27 | User: "admin", 28 | Auth: []ssh.AuthMethod{ 29 | ssh.PublicKeys(key), 30 | }, 31 | } 32 | 33 | hostAndPort := host + ":" + strconv.Itoa(port) 34 | connection, err := ssh.Dial("tcp", hostAndPort, sshConfig) 35 | CheckError(err) 36 | defer connection.Close() 37 | 38 | session, err := connection.NewSession() 39 | CheckError(err) 40 | defer session.Close() 41 | 42 | stdout, err := session.StdoutPipe() 43 | CheckError(err) 44 | 45 | var buf bytes.Buffer 46 | go io.Copy(&buf, stdout) 47 | 48 | session.Run("jfrog-authenticate") 49 | 50 | var result SshAuthResult 51 | err = json.Unmarshal(buf.Bytes(), &result) 52 | CheckError(err) 53 | details.Url = AddTrailingSlashIfNeeded(result.Href) 54 | details.SshAuthHeaders = result.Headers 55 | fmt.Println("SSH authentication successful.") 56 | } 57 | 58 | func parseUrl(url string) (protocol, host string, port int) { 59 | pattern1 := "^(.+)://(.+):([0-9].+)/$" 60 | pattern2 := "^(.+)://(.+)$" 61 | 62 | r, err := regexp.Compile(pattern1) 63 | CheckError(err) 64 | groups := r.FindStringSubmatch(url) 65 | if len(groups) == 4 { 66 | protocol = groups[1] 67 | host = groups[2] 68 | port, err = strconv.Atoi(groups[3]) 69 | if err != nil { 70 | Exit(ExitCodeError, "URL: " + url + " is invalid. Expecting ssh://: or http(s)://...") 71 | } 72 | return 73 | } 74 | 75 | r, err = regexp.Compile(pattern2) 76 | CheckError(err) 77 | groups = r.FindStringSubmatch(url) 78 | if len(groups) == 3 { 79 | protocol = groups[1] 80 | host = groups[2] 81 | port = 80 82 | return 83 | } 84 | return 85 | } 86 | 87 | type SshAuthResult struct { 88 | Href string 89 | Headers map[string]string 90 | } -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/buffer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ssh 6 | 7 | import ( 8 | "io" 9 | "sync" 10 | ) 11 | 12 | // buffer provides a linked list buffer for data exchange 13 | // between producer and consumer. Theoretically the buffer is 14 | // of unlimited capacity as it does no allocation of its own. 15 | type buffer struct { 16 | // protects concurrent access to head, tail and closed 17 | *sync.Cond 18 | 19 | head *element // the buffer that will be read first 20 | tail *element // the buffer that will be read last 21 | 22 | closed bool 23 | } 24 | 25 | // An element represents a single link in a linked list. 26 | type element struct { 27 | buf []byte 28 | next *element 29 | } 30 | 31 | // newBuffer returns an empty buffer that is not closed. 32 | func newBuffer() *buffer { 33 | e := new(element) 34 | b := &buffer{ 35 | Cond: newCond(), 36 | head: e, 37 | tail: e, 38 | } 39 | return b 40 | } 41 | 42 | // write makes buf available for Read to receive. 43 | // buf must not be modified after the call to write. 44 | func (b *buffer) write(buf []byte) { 45 | b.Cond.L.Lock() 46 | e := &element{buf: buf} 47 | b.tail.next = e 48 | b.tail = e 49 | b.Cond.Signal() 50 | b.Cond.L.Unlock() 51 | } 52 | 53 | // eof closes the buffer. Reads from the buffer once all 54 | // the data has been consumed will receive os.EOF. 55 | func (b *buffer) eof() error { 56 | b.Cond.L.Lock() 57 | b.closed = true 58 | b.Cond.Signal() 59 | b.Cond.L.Unlock() 60 | return nil 61 | } 62 | 63 | // Read reads data from the internal buffer in buf. Reads will block 64 | // if no data is available, or until the buffer is closed. 65 | func (b *buffer) Read(buf []byte) (n int, err error) { 66 | b.Cond.L.Lock() 67 | defer b.Cond.L.Unlock() 68 | 69 | for len(buf) > 0 { 70 | // if there is data in b.head, copy it 71 | if len(b.head.buf) > 0 { 72 | r := copy(buf, b.head.buf) 73 | buf, b.head.buf = buf[r:], b.head.buf[r:] 74 | n += r 75 | continue 76 | } 77 | // if there is a next buffer, make it the head 78 | if len(b.head.buf) == 0 && b.head != b.tail { 79 | b.head = b.head.next 80 | continue 81 | } 82 | 83 | // if at least one byte has been copied, return 84 | if n > 0 { 85 | break 86 | } 87 | 88 | // if nothing was read, and there is nothing outstanding 89 | // check to see if the buffer is closed. 90 | if b.closed { 91 | err = io.EOF 92 | break 93 | } 94 | // out of buffers, wait for producer 95 | b.Cond.Wait() 96 | } 97 | return 98 | } 99 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/agent/forward.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package agent 6 | 7 | import ( 8 | "errors" 9 | "io" 10 | "net" 11 | "sync" 12 | 13 | "github.com/JFrogDev/artifactory-cli-go/Godeps/_workspace/src/golang.org/x/crypto/ssh" 14 | ) 15 | 16 | // RequestAgentForwarding sets up agent forwarding for the session. 17 | // ForwardToAgent or ForwardToRemote should be called to route 18 | // the authentication requests. 19 | func RequestAgentForwarding(session *ssh.Session) error { 20 | ok, err := session.SendRequest("auth-agent-req@openssh.com", true, nil) 21 | if err != nil { 22 | return err 23 | } 24 | if !ok { 25 | return errors.New("forwarding request denied") 26 | } 27 | return nil 28 | } 29 | 30 | // ForwardToAgent routes authentication requests to the given keyring. 31 | func ForwardToAgent(client *ssh.Client, keyring Agent) error { 32 | channels := client.HandleChannelOpen(channelType) 33 | if channels == nil { 34 | return errors.New("agent: already have handler for " + channelType) 35 | } 36 | 37 | go func() { 38 | for ch := range channels { 39 | channel, reqs, err := ch.Accept() 40 | if err != nil { 41 | continue 42 | } 43 | go ssh.DiscardRequests(reqs) 44 | go func() { 45 | ServeAgent(keyring, channel) 46 | channel.Close() 47 | }() 48 | } 49 | }() 50 | return nil 51 | } 52 | 53 | const channelType = "auth-agent@openssh.com" 54 | 55 | // ForwardToRemote routes authentication requests to the ssh-agent 56 | // process serving on the given unix socket. 57 | func ForwardToRemote(client *ssh.Client, addr string) error { 58 | channels := client.HandleChannelOpen(channelType) 59 | if channels == nil { 60 | return errors.New("agent: already have handler for " + channelType) 61 | } 62 | conn, err := net.Dial("unix", addr) 63 | if err != nil { 64 | return err 65 | } 66 | conn.Close() 67 | 68 | go func() { 69 | for ch := range channels { 70 | channel, reqs, err := ch.Accept() 71 | if err != nil { 72 | continue 73 | } 74 | go ssh.DiscardRequests(reqs) 75 | go forwardUnixSocket(channel, addr) 76 | } 77 | }() 78 | return nil 79 | } 80 | 81 | func forwardUnixSocket(channel ssh.Channel, addr string) { 82 | conn, err := net.Dial("unix", addr) 83 | if err != nil { 84 | return 85 | } 86 | 87 | var wg sync.WaitGroup 88 | wg.Add(2) 89 | go func() { 90 | io.Copy(conn, channel) 91 | conn.(*net.UnixConn).CloseWrite() 92 | wg.Done() 93 | }() 94 | go func() { 95 | io.Copy(channel, conn) 96 | channel.CloseWrite() 97 | wg.Done() 98 | }() 99 | 100 | wg.Wait() 101 | conn.Close() 102 | channel.Close() 103 | } 104 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/curve25519/square_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This code was translated into a form compatible with 6a from the public 6 | // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html 7 | 8 | // +build amd64,!gccgo,!appengine 9 | 10 | // func square(out, in *[5]uint64) 11 | TEXT ·square(SB),7,$96-16 12 | MOVQ out+0(FP), DI 13 | MOVQ in+8(FP), SI 14 | 15 | MOVQ SP,R11 16 | MOVQ $31,CX 17 | NOTQ CX 18 | ANDQ CX,SP 19 | ADDQ $32, SP 20 | 21 | MOVQ R11,0(SP) 22 | MOVQ R12,8(SP) 23 | MOVQ R13,16(SP) 24 | MOVQ R14,24(SP) 25 | MOVQ R15,32(SP) 26 | MOVQ BX,40(SP) 27 | MOVQ BP,48(SP) 28 | MOVQ 0(SI),AX 29 | MULQ 0(SI) 30 | MOVQ AX,CX 31 | MOVQ DX,R8 32 | MOVQ 0(SI),AX 33 | SHLQ $1,AX 34 | MULQ 8(SI) 35 | MOVQ AX,R9 36 | MOVQ DX,R10 37 | MOVQ 0(SI),AX 38 | SHLQ $1,AX 39 | MULQ 16(SI) 40 | MOVQ AX,R11 41 | MOVQ DX,R12 42 | MOVQ 0(SI),AX 43 | SHLQ $1,AX 44 | MULQ 24(SI) 45 | MOVQ AX,R13 46 | MOVQ DX,R14 47 | MOVQ 0(SI),AX 48 | SHLQ $1,AX 49 | MULQ 32(SI) 50 | MOVQ AX,R15 51 | MOVQ DX,BX 52 | MOVQ 8(SI),AX 53 | MULQ 8(SI) 54 | ADDQ AX,R11 55 | ADCQ DX,R12 56 | MOVQ 8(SI),AX 57 | SHLQ $1,AX 58 | MULQ 16(SI) 59 | ADDQ AX,R13 60 | ADCQ DX,R14 61 | MOVQ 8(SI),AX 62 | SHLQ $1,AX 63 | MULQ 24(SI) 64 | ADDQ AX,R15 65 | ADCQ DX,BX 66 | MOVQ 8(SI),DX 67 | IMUL3Q $38,DX,AX 68 | MULQ 32(SI) 69 | ADDQ AX,CX 70 | ADCQ DX,R8 71 | MOVQ 16(SI),AX 72 | MULQ 16(SI) 73 | ADDQ AX,R15 74 | ADCQ DX,BX 75 | MOVQ 16(SI),DX 76 | IMUL3Q $38,DX,AX 77 | MULQ 24(SI) 78 | ADDQ AX,CX 79 | ADCQ DX,R8 80 | MOVQ 16(SI),DX 81 | IMUL3Q $38,DX,AX 82 | MULQ 32(SI) 83 | ADDQ AX,R9 84 | ADCQ DX,R10 85 | MOVQ 24(SI),DX 86 | IMUL3Q $19,DX,AX 87 | MULQ 24(SI) 88 | ADDQ AX,R9 89 | ADCQ DX,R10 90 | MOVQ 24(SI),DX 91 | IMUL3Q $38,DX,AX 92 | MULQ 32(SI) 93 | ADDQ AX,R11 94 | ADCQ DX,R12 95 | MOVQ 32(SI),DX 96 | IMUL3Q $19,DX,AX 97 | MULQ 32(SI) 98 | ADDQ AX,R13 99 | ADCQ DX,R14 100 | MOVQ ·REDMASK51(SB),SI 101 | SHLQ $13,R8:CX 102 | ANDQ SI,CX 103 | SHLQ $13,R10:R9 104 | ANDQ SI,R9 105 | ADDQ R8,R9 106 | SHLQ $13,R12:R11 107 | ANDQ SI,R11 108 | ADDQ R10,R11 109 | SHLQ $13,R14:R13 110 | ANDQ SI,R13 111 | ADDQ R12,R13 112 | SHLQ $13,BX:R15 113 | ANDQ SI,R15 114 | ADDQ R14,R15 115 | IMUL3Q $19,BX,DX 116 | ADDQ DX,CX 117 | MOVQ CX,DX 118 | SHRQ $51,DX 119 | ADDQ R9,DX 120 | ANDQ SI,CX 121 | MOVQ DX,R8 122 | SHRQ $51,DX 123 | ADDQ R11,DX 124 | ANDQ SI,R8 125 | MOVQ DX,R9 126 | SHRQ $51,DX 127 | ADDQ R13,DX 128 | ANDQ SI,R9 129 | MOVQ DX,AX 130 | SHRQ $51,DX 131 | ADDQ R15,DX 132 | ANDQ SI,AX 133 | MOVQ DX,R10 134 | SHRQ $51,DX 135 | IMUL3Q $19,DX,DX 136 | ADDQ DX,CX 137 | ANDQ SI,R10 138 | MOVQ CX,0(DI) 139 | MOVQ R8,8(DI) 140 | MOVQ R9,16(DI) 141 | MOVQ AX,24(DI) 142 | MOVQ R10,32(DI) 143 | MOVQ 0(SP),R11 144 | MOVQ 8(SP),R12 145 | MOVQ 16(SP),R13 146 | MOVQ 24(SP),R14 147 | MOVQ 32(SP),R15 148 | MOVQ 40(SP),BX 149 | MOVQ 48(SP),BP 150 | MOVQ R11,SP 151 | MOVQ DI,AX 152 | MOVQ SI,DX 153 | RET 154 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/curve25519/mul_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This code was translated into a form compatible with 6a from the public 6 | // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html 7 | 8 | // +build amd64,!gccgo,!appengine 9 | 10 | // func mul(dest, a, b *[5]uint64) 11 | TEXT ·mul(SB),0,$128-24 12 | MOVQ dest+0(FP), DI 13 | MOVQ a+8(FP), SI 14 | MOVQ b+16(FP), DX 15 | 16 | MOVQ SP,R11 17 | MOVQ $31,CX 18 | NOTQ CX 19 | ANDQ CX,SP 20 | ADDQ $32,SP 21 | 22 | MOVQ R11,0(SP) 23 | MOVQ R12,8(SP) 24 | MOVQ R13,16(SP) 25 | MOVQ R14,24(SP) 26 | MOVQ R15,32(SP) 27 | MOVQ BX,40(SP) 28 | MOVQ BP,48(SP) 29 | MOVQ DI,56(SP) 30 | MOVQ DX,CX 31 | MOVQ 24(SI),DX 32 | IMUL3Q $19,DX,AX 33 | MOVQ AX,64(SP) 34 | MULQ 16(CX) 35 | MOVQ AX,R8 36 | MOVQ DX,R9 37 | MOVQ 32(SI),DX 38 | IMUL3Q $19,DX,AX 39 | MOVQ AX,72(SP) 40 | MULQ 8(CX) 41 | ADDQ AX,R8 42 | ADCQ DX,R9 43 | MOVQ 0(SI),AX 44 | MULQ 0(CX) 45 | ADDQ AX,R8 46 | ADCQ DX,R9 47 | MOVQ 0(SI),AX 48 | MULQ 8(CX) 49 | MOVQ AX,R10 50 | MOVQ DX,R11 51 | MOVQ 0(SI),AX 52 | MULQ 16(CX) 53 | MOVQ AX,R12 54 | MOVQ DX,R13 55 | MOVQ 0(SI),AX 56 | MULQ 24(CX) 57 | MOVQ AX,R14 58 | MOVQ DX,R15 59 | MOVQ 0(SI),AX 60 | MULQ 32(CX) 61 | MOVQ AX,BX 62 | MOVQ DX,BP 63 | MOVQ 8(SI),AX 64 | MULQ 0(CX) 65 | ADDQ AX,R10 66 | ADCQ DX,R11 67 | MOVQ 8(SI),AX 68 | MULQ 8(CX) 69 | ADDQ AX,R12 70 | ADCQ DX,R13 71 | MOVQ 8(SI),AX 72 | MULQ 16(CX) 73 | ADDQ AX,R14 74 | ADCQ DX,R15 75 | MOVQ 8(SI),AX 76 | MULQ 24(CX) 77 | ADDQ AX,BX 78 | ADCQ DX,BP 79 | MOVQ 8(SI),DX 80 | IMUL3Q $19,DX,AX 81 | MULQ 32(CX) 82 | ADDQ AX,R8 83 | ADCQ DX,R9 84 | MOVQ 16(SI),AX 85 | MULQ 0(CX) 86 | ADDQ AX,R12 87 | ADCQ DX,R13 88 | MOVQ 16(SI),AX 89 | MULQ 8(CX) 90 | ADDQ AX,R14 91 | ADCQ DX,R15 92 | MOVQ 16(SI),AX 93 | MULQ 16(CX) 94 | ADDQ AX,BX 95 | ADCQ DX,BP 96 | MOVQ 16(SI),DX 97 | IMUL3Q $19,DX,AX 98 | MULQ 24(CX) 99 | ADDQ AX,R8 100 | ADCQ DX,R9 101 | MOVQ 16(SI),DX 102 | IMUL3Q $19,DX,AX 103 | MULQ 32(CX) 104 | ADDQ AX,R10 105 | ADCQ DX,R11 106 | MOVQ 24(SI),AX 107 | MULQ 0(CX) 108 | ADDQ AX,R14 109 | ADCQ DX,R15 110 | MOVQ 24(SI),AX 111 | MULQ 8(CX) 112 | ADDQ AX,BX 113 | ADCQ DX,BP 114 | MOVQ 64(SP),AX 115 | MULQ 24(CX) 116 | ADDQ AX,R10 117 | ADCQ DX,R11 118 | MOVQ 64(SP),AX 119 | MULQ 32(CX) 120 | ADDQ AX,R12 121 | ADCQ DX,R13 122 | MOVQ 32(SI),AX 123 | MULQ 0(CX) 124 | ADDQ AX,BX 125 | ADCQ DX,BP 126 | MOVQ 72(SP),AX 127 | MULQ 16(CX) 128 | ADDQ AX,R10 129 | ADCQ DX,R11 130 | MOVQ 72(SP),AX 131 | MULQ 24(CX) 132 | ADDQ AX,R12 133 | ADCQ DX,R13 134 | MOVQ 72(SP),AX 135 | MULQ 32(CX) 136 | ADDQ AX,R14 137 | ADCQ DX,R15 138 | MOVQ ·REDMASK51(SB),SI 139 | SHLQ $13,R9:R8 140 | ANDQ SI,R8 141 | SHLQ $13,R11:R10 142 | ANDQ SI,R10 143 | ADDQ R9,R10 144 | SHLQ $13,R13:R12 145 | ANDQ SI,R12 146 | ADDQ R11,R12 147 | SHLQ $13,R15:R14 148 | ANDQ SI,R14 149 | ADDQ R13,R14 150 | SHLQ $13,BP:BX 151 | ANDQ SI,BX 152 | ADDQ R15,BX 153 | IMUL3Q $19,BP,DX 154 | ADDQ DX,R8 155 | MOVQ R8,DX 156 | SHRQ $51,DX 157 | ADDQ R10,DX 158 | MOVQ DX,CX 159 | SHRQ $51,DX 160 | ANDQ SI,R8 161 | ADDQ R12,DX 162 | MOVQ DX,R9 163 | SHRQ $51,DX 164 | ANDQ SI,CX 165 | ADDQ R14,DX 166 | MOVQ DX,AX 167 | SHRQ $51,DX 168 | ANDQ SI,R9 169 | ADDQ BX,DX 170 | MOVQ DX,R10 171 | SHRQ $51,DX 172 | ANDQ SI,AX 173 | IMUL3Q $19,DX,DX 174 | ADDQ DX,R8 175 | ANDQ SI,R10 176 | MOVQ R8,0(DI) 177 | MOVQ CX,8(DI) 178 | MOVQ R9,16(DI) 179 | MOVQ AX,24(DI) 180 | MOVQ R10,32(DI) 181 | MOVQ 0(SP),R11 182 | MOVQ 8(SP),R12 183 | MOVQ 16(SP),R13 184 | MOVQ 24(SP),R14 185 | MOVQ 32(SP),R15 186 | MOVQ 40(SP),BX 187 | MOVQ 48(SP),BP 188 | MOVQ R11,SP 189 | MOVQ DI,AX 190 | MOVQ SI,DX 191 | RET 192 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/connection.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ssh 6 | 7 | import ( 8 | "fmt" 9 | "net" 10 | ) 11 | 12 | // OpenChannelError is returned if the other side rejects an 13 | // OpenChannel request. 14 | type OpenChannelError struct { 15 | Reason RejectionReason 16 | Message string 17 | } 18 | 19 | func (e *OpenChannelError) Error() string { 20 | return fmt.Sprintf("ssh: rejected: %s (%s)", e.Reason, e.Message) 21 | } 22 | 23 | // ConnMetadata holds metadata for the connection. 24 | type ConnMetadata interface { 25 | // User returns the user ID for this connection. 26 | // It is empty if no authentication is used. 27 | User() string 28 | 29 | // SessionID returns the sesson hash, also denoted by H. 30 | SessionID() []byte 31 | 32 | // ClientVersion returns the client's version string as hashed 33 | // into the session ID. 34 | ClientVersion() []byte 35 | 36 | // ServerVersion returns the server's version string as hashed 37 | // into the session ID. 38 | ServerVersion() []byte 39 | 40 | // RemoteAddr returns the remote address for this connection. 41 | RemoteAddr() net.Addr 42 | 43 | // LocalAddr returns the local address for this connection. 44 | LocalAddr() net.Addr 45 | } 46 | 47 | // Conn represents an SSH connection for both server and client roles. 48 | // Conn is the basis for implementing an application layer, such 49 | // as ClientConn, which implements the traditional shell access for 50 | // clients. 51 | type Conn interface { 52 | ConnMetadata 53 | 54 | // SendRequest sends a global request, and returns the 55 | // reply. If wantReply is true, it returns the response status 56 | // and payload. See also RFC4254, section 4. 57 | SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) 58 | 59 | // OpenChannel tries to open an channel. If the request is 60 | // rejected, it returns *OpenChannelError. On success it returns 61 | // the SSH Channel and a Go channel for incoming, out-of-band 62 | // requests. The Go channel must be serviced, or the 63 | // connection will hang. 64 | OpenChannel(name string, data []byte) (Channel, <-chan *Request, error) 65 | 66 | // Close closes the underlying network connection 67 | Close() error 68 | 69 | // Wait blocks until the connection has shut down, and returns the 70 | // error causing the shutdown. 71 | Wait() error 72 | 73 | // TODO(hanwen): consider exposing: 74 | // RequestKeyChange 75 | // Disconnect 76 | } 77 | 78 | // DiscardRequests consumes and rejects all requests from the 79 | // passed-in channel. 80 | func DiscardRequests(in <-chan *Request) { 81 | for req := range in { 82 | if req.WantReply { 83 | req.Reply(false, nil) 84 | } 85 | } 86 | } 87 | 88 | // A connection represents an incoming connection. 89 | type connection struct { 90 | transport *handshakeTransport 91 | sshConn 92 | 93 | // The connection protocol. 94 | *mux 95 | } 96 | 97 | func (c *connection) Close() error { 98 | return c.sshConn.conn.Close() 99 | } 100 | 101 | // sshconn provides net.Conn metadata, but disallows direct reads and 102 | // writes. 103 | type sshConn struct { 104 | conn net.Conn 105 | 106 | user string 107 | sessionID []byte 108 | clientVersion []byte 109 | serverVersion []byte 110 | } 111 | 112 | func dup(src []byte) []byte { 113 | dst := make([]byte, len(src)) 114 | copy(dst, src) 115 | return dst 116 | } 117 | 118 | func (c *sshConn) User() string { 119 | return c.user 120 | } 121 | 122 | func (c *sshConn) RemoteAddr() net.Addr { 123 | return c.conn.RemoteAddr() 124 | } 125 | 126 | func (c *sshConn) Close() error { 127 | return c.conn.Close() 128 | } 129 | 130 | func (c *sshConn) LocalAddr() net.Addr { 131 | return c.conn.LocalAddr() 132 | } 133 | 134 | func (c *sshConn) SessionID() []byte { 135 | return dup(c.sessionID) 136 | } 137 | 138 | func (c *sshConn) ClientVersion() []byte { 139 | return dup(c.clientVersion) 140 | } 141 | 142 | func (c *sshConn) ServerVersion() []byte { 143 | return dup(c.serverVersion) 144 | } 145 | -------------------------------------------------------------------------------- /commands/download.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "strconv" 7 | "encoding/json" 8 | "github.com/JFrogDev/artifactory-cli-go/utils" 9 | ) 10 | 11 | // Downloads the artifacts using the specified download pattern. 12 | // Returns the AQL query used for the download. 13 | func Download(downloadPattern string, flags *utils.Flags) string { 14 | if flags.ArtDetails.SshKeyPath != "" { 15 | utils.SshAuthentication(flags.ArtDetails) 16 | } 17 | 18 | aqlUrl := flags.ArtDetails.Url + "api/search/aql" 19 | data := utils.BuildAqlSearchQuery(downloadPattern, flags.Recursive, flags.Props) 20 | fmt.Println("Searching Artifactory using AQL query: " + data) 21 | 22 | if !flags.DryRun { 23 | resp, json := utils.SendPost(aqlUrl, []byte(data), *flags.ArtDetails) 24 | fmt.Println("Artifactory response:", resp.Status) 25 | 26 | if resp.StatusCode == 200 { 27 | resultItems := parseAqlSearchResponse(json) 28 | downloadFiles(resultItems, flags) 29 | fmt.Println("Downloaded " + strconv.Itoa(len(resultItems)) + " artifacts from Artifactory.") 30 | } 31 | } 32 | return data 33 | } 34 | 35 | func downloadFiles(resultItems []AqlSearchResultItem, flags *utils.Flags) { 36 | size := len(resultItems) 37 | var wg sync.WaitGroup 38 | for i := 0; i < flags.Threads; i++ { 39 | wg.Add(1) 40 | go func(threadId int) { 41 | for j := threadId; j < size; j += flags.Threads { 42 | downloadPath := buildDownloadUrl(flags.ArtDetails.Url, resultItems[j]) 43 | logMsgPrefix := utils.GetLogMsgPrefix(threadId, flags.DryRun) 44 | fmt.Println(logMsgPrefix + " Downloading " + downloadPath) 45 | if !flags.DryRun { 46 | downloadFile(downloadPath, resultItems[j].Path, resultItems[j].Name, logMsgPrefix, flags) 47 | } 48 | } 49 | wg.Done() 50 | }(i) 51 | } 52 | wg.Wait() 53 | } 54 | 55 | func downloadFile(downloadPath, localPath, localFileName, logMsgPrefix string, flags *utils.Flags) { 56 | details := utils.GetFileDetailsFromArtifactory(downloadPath, *flags.ArtDetails) 57 | localFilePath := localPath + "/" + localFileName 58 | if shouldDownloadFile(localFilePath, details, flags.ArtDetails.User, flags.ArtDetails.Password) { 59 | if flags.SplitCount == 0 || flags.MinSplitSize < 0 || flags.MinSplitSize*1000 > details.Size || !details.AcceptRanges { 60 | resp := utils.DownloadFile(downloadPath, localPath, localFileName, flags.Flat, *flags.ArtDetails) 61 | fmt.Println(logMsgPrefix + " Artifactory response:", resp.Status) 62 | } else { 63 | utils.DownloadFileConcurrently( 64 | downloadPath, localPath, localFileName, logMsgPrefix, details.Size, flags) 65 | } 66 | } else { 67 | fmt.Println(logMsgPrefix + " File already exists locally.") 68 | } 69 | } 70 | 71 | func buildDownloadUrl(baseUrl string, resultItem AqlSearchResultItem) string { 72 | if resultItem.Path == "." { 73 | return baseUrl + resultItem.Repo + "/" + resultItem.Name 74 | } 75 | return baseUrl + resultItem.Repo + "/" + resultItem.Path + "/" + resultItem.Name 76 | } 77 | 78 | func shouldDownloadFile(localFilePath string, artifactoryFileDetails *utils.FileDetails, user string, password string) bool { 79 | if !utils.IsFileExists(localFilePath) { 80 | return true 81 | } 82 | localFileDetails := utils.GetFileDetails(localFilePath) 83 | if localFileDetails.Md5 != artifactoryFileDetails.Md5 || localFileDetails.Sha1 != artifactoryFileDetails.Sha1 { 84 | return true 85 | } 86 | return false 87 | } 88 | 89 | func parseAqlSearchResponse(resp []byte) []AqlSearchResultItem { 90 | var result AqlSearchResult 91 | err := json.Unmarshal(resp, &result) 92 | utils.CheckError(err) 93 | return result.Results 94 | } 95 | 96 | type AqlSearchResult struct { 97 | Results []AqlSearchResultItem 98 | } 99 | 100 | type AqlSearchResultItem struct { 101 | Repo string 102 | Path string 103 | Name string 104 | } -------------------------------------------------------------------------------- /utils/artifactoryUtils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "os" 5 | "io" 6 | "fmt" 7 | "sync" 8 | "strconv" 9 | "net/http" 10 | "crypto/md5" 11 | "crypto/sha1" 12 | "encoding/hex" 13 | ) 14 | 15 | func GetFileDetails(filePath string) *FileDetails { 16 | details := new(FileDetails) 17 | details.Md5 = calcMd5(filePath) 18 | details.Sha1 = calcSha1(filePath) 19 | 20 | file, err := os.Open(filePath) 21 | CheckError(err) 22 | defer file.Close() 23 | 24 | fileInfo, err := file.Stat() 25 | CheckError(err) 26 | details.Size = fileInfo.Size() 27 | 28 | return details 29 | } 30 | 31 | func calcSha1(filePath string) string { 32 | file, err := os.Open(filePath) 33 | CheckError(err) 34 | defer file.Close() 35 | 36 | var resSha1 []byte 37 | hashSha1 := sha1.New() 38 | _, err = io.Copy(hashSha1, file) 39 | CheckError(err) 40 | return hex.EncodeToString(hashSha1.Sum(resSha1)) 41 | } 42 | 43 | func calcMd5(filePath string) string { 44 | file, err := os.Open(filePath) 45 | CheckError(err) 46 | defer file.Close() 47 | 48 | var resMd5 []byte 49 | hashMd5 := md5.New() 50 | _, err = io.Copy(hashMd5, file) 51 | CheckError(err) 52 | return hex.EncodeToString(hashMd5.Sum(resMd5)) 53 | } 54 | 55 | func GetFileDetailsFromArtifactory(downloadUrl string, artifactoryDetails ArtifactoryDetails) *FileDetails { 56 | resp, _ := SendHead(downloadUrl, artifactoryDetails) 57 | fileSize, err := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64) 58 | CheckError(err) 59 | 60 | fileDetails := new(FileDetails) 61 | 62 | fileDetails.Md5 = resp.Header.Get("X-Checksum-Md5") 63 | fileDetails.Sha1 = resp.Header.Get("X-Checksum-Sha1") 64 | fileDetails.Size = fileSize 65 | fileDetails.AcceptRanges = resp.Header.Get("Accept-Ranges") == "bytes" 66 | return fileDetails 67 | } 68 | 69 | func GetEncryptedPasswordFromArtifactory(artifactoryDetails *ArtifactoryDetails) (*http.Response, string) { 70 | apiUrl := artifactoryDetails.Url + "api/security/encryptedPassword" 71 | resp, body := SendGet(apiUrl, nil, *artifactoryDetails) 72 | return resp, string(body) 73 | } 74 | 75 | func DownloadFileConcurrently(downloadPath, localPath, fileName, logMsgPrefix string, fileSize int64, flags *Flags) { 76 | tempLoclPath := GetTempDirPath() + "/" + localPath 77 | 78 | var wg sync.WaitGroup 79 | chunkSize := fileSize / int64(flags.SplitCount) 80 | mod := fileSize % int64(flags.SplitCount) 81 | 82 | for i := 0; i < flags.SplitCount ; i++ { 83 | wg.Add(1) 84 | start := chunkSize * int64(i) 85 | end := chunkSize * (int64(i) + 1) 86 | if i == flags.SplitCount-1 { 87 | end += mod 88 | } 89 | go func(start, end int64, i int) { 90 | headers := make(map[string]string) 91 | headers["Range"] = "bytes=" + strconv.FormatInt(start, 10) +"-" + strconv.FormatInt(end-1, 10) 92 | resp, body := SendGet(downloadPath, headers, *flags.ArtDetails) 93 | 94 | fmt.Println(logMsgPrefix + " [" + strconv.Itoa(i) + "]:", resp.Status + "...") 95 | 96 | os.MkdirAll(tempLoclPath ,0777) 97 | filePath := tempLoclPath + "/" + fileName + "_" + strconv.Itoa(i) 98 | 99 | createFileWithContent(filePath, body) 100 | wg.Done() 101 | }(start, end, i) 102 | } 103 | wg.Wait() 104 | 105 | if !flags.Flat && localPath != "" { 106 | os.MkdirAll(localPath ,0777) 107 | fileName = localPath + "/" + fileName 108 | } 109 | 110 | if IsPathExists(fileName) { 111 | err := os.Remove(fileName) 112 | CheckError(err) 113 | } 114 | 115 | destFile, err := os.Create(fileName) 116 | CheckError(err) 117 | defer destFile.Close() 118 | for i := 0; i < flags.SplitCount; i++ { 119 | tempFilePath := GetTempDirPath() + "/" + fileName + "_" + strconv.Itoa(i) 120 | AppendFile(tempFilePath, destFile) 121 | } 122 | fmt.Println(logMsgPrefix + " Done downloading.") 123 | } 124 | 125 | func createFileWithContent(filePath string, content []byte) { 126 | out, err := os.Create(filePath) 127 | CheckError(err) 128 | defer out.Close() 129 | out.Write(content) 130 | } 131 | 132 | type FileDetails struct { 133 | Md5 string 134 | Sha1 string 135 | Size int64 136 | AcceptRanges bool 137 | } -------------------------------------------------------------------------------- /commands/config.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "os" 5 | "fmt" 6 | "bytes" 7 | "strings" 8 | "syscall" 9 | "io/ioutil" 10 | "encoding/json" 11 | "github.com/JFrogDev/artifactory-cli-go/utils" 12 | "github.com/JFrogDev/artifactory-cli-go/Godeps/_workspace/src/golang.org/x/crypto/ssh/terminal" 13 | ) 14 | 15 | func Config(details *utils.ArtifactoryDetails, interactive, shouldEncPassword bool) { 16 | if interactive { 17 | if details.Url == "" { 18 | print("Artifactory URL: ") 19 | fmt.Scanln(&details.Url) 20 | } 21 | 22 | if strings.Index(details.Url, "ssh://") == 0 || strings.Index(details.Url, "SSH://") == 0 { 23 | readSshKeyPathFromConsole(details) 24 | } else { 25 | readCredentialsFromConsole(details) 26 | } 27 | } 28 | details.Url = utils.AddTrailingSlashIfNeeded(details.Url) 29 | if shouldEncPassword { 30 | details = encryptPassword(details) 31 | } 32 | writeConfFile(details) 33 | } 34 | 35 | func readSshKeyPathFromConsole(details *utils.ArtifactoryDetails) { 36 | if details.SshKeyPath == "" { 37 | print("SSH key file path: ") 38 | fmt.Scanln(&details.SshKeyPath) 39 | } 40 | if !utils.IsFileExists(details.SshKeyPath) { 41 | fmt.Println("Warning: Could not find SSH key file at: " + details.SshKeyPath) 42 | } 43 | } 44 | 45 | func readCredentialsFromConsole(details *utils.ArtifactoryDetails) { 46 | if details.User == "" { 47 | print("User: ") 48 | fmt.Scanln(&details.User) 49 | } 50 | if details.Password == "" { 51 | print("Password: ") 52 | bytePassword, err := terminal.ReadPassword(int(syscall.Stdin)) 53 | details.Password = string(bytePassword) 54 | utils.CheckError(err) 55 | } 56 | } 57 | 58 | func ShowConfig() { 59 | details := readConfFile() 60 | if details.Url != "" { 61 | fmt.Println("Url: " + details.Url) 62 | } 63 | if details.User != "" { 64 | fmt.Println("User: " + details.User) 65 | } 66 | if details.Password != "" { 67 | fmt.Println("Password: " + details.Password) 68 | } 69 | if details.SshKeyPath != "" { 70 | fmt.Println("SSH key file path: " + details.SshKeyPath) 71 | } 72 | } 73 | 74 | func ClearConfig() { 75 | writeConfFile(new(utils.ArtifactoryDetails)) 76 | } 77 | 78 | func GetConfig() *utils.ArtifactoryDetails { 79 | return readConfFile() 80 | } 81 | 82 | func encryptPassword(details *utils.ArtifactoryDetails) *utils.ArtifactoryDetails { 83 | if details.Password == "" { 84 | return details 85 | } 86 | response, encPassword := utils.GetEncryptedPasswordFromArtifactory(details) 87 | switch response.StatusCode { 88 | case 409: 89 | utils.Exit(utils.ExitCodeError, "\nYour Artifactory server is not configured to encrypt passwords.\n" + 90 | "You may use \"art config --enc-password=false\"") 91 | case 200: 92 | details.Password = encPassword 93 | default: 94 | utils.Exit(utils.ExitCodeError, "\nArtifactory response: " + response.Status) 95 | } 96 | return details 97 | } 98 | 99 | func getConFilePath() string { 100 | userDir := utils.GetHomeDir() 101 | if userDir == "" { 102 | utils.Exit(utils.ExitCodeError, "Couldn't find home directory. Make sure your HOME environment variable is set.") 103 | } 104 | confPath := userDir + "/.jfrog/" 105 | os.MkdirAll(confPath ,0777) 106 | return confPath + "art-cli.conf" 107 | } 108 | 109 | func writeConfFile(details *utils.ArtifactoryDetails) { 110 | confFilePath := getConFilePath() 111 | if !utils.IsFileExists(confFilePath) { 112 | out, err := os.Create(confFilePath) 113 | utils.CheckError(err) 114 | defer out.Close() 115 | } 116 | 117 | b, err := json.Marshal(&details) 118 | utils.CheckError(err) 119 | var content bytes.Buffer 120 | err = json.Indent(&content, b, "", " ") 121 | utils.CheckError(err) 122 | 123 | ioutil.WriteFile(confFilePath,[]byte(content.String()), 0x777) 124 | } 125 | 126 | func readConfFile() *utils.ArtifactoryDetails { 127 | confFilePath := getConFilePath() 128 | details := new(utils.ArtifactoryDetails) 129 | if !utils.IsFileExists(confFilePath) { 130 | return details 131 | } 132 | content := utils.ReadFile(confFilePath) 133 | json.Unmarshal(content, &details) 134 | 135 | return details 136 | } -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/terminal/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin dragonfly freebsd linux,!appengine netbsd openbsd 6 | 7 | // Package terminal provides support functions for dealing with terminals, as 8 | // commonly found on UNIX systems. 9 | // 10 | // Putting a terminal into raw mode is the most common requirement: 11 | // 12 | // oldState, err := terminal.MakeRaw(0) 13 | // if err != nil { 14 | // panic(err) 15 | // } 16 | // defer terminal.Restore(0, oldState) 17 | package terminal 18 | 19 | import ( 20 | "io" 21 | "syscall" 22 | "unsafe" 23 | ) 24 | 25 | // State contains the state of a terminal. 26 | type State struct { 27 | termios syscall.Termios 28 | } 29 | 30 | // IsTerminal returns true if the given file descriptor is a terminal. 31 | func IsTerminal(fd int) bool { 32 | var termios syscall.Termios 33 | _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) 34 | return err == 0 35 | } 36 | 37 | // MakeRaw put the terminal connected to the given file descriptor into raw 38 | // mode and returns the previous state of the terminal so that it can be 39 | // restored. 40 | func MakeRaw(fd int) (*State, error) { 41 | var oldState State 42 | if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { 43 | return nil, err 44 | } 45 | 46 | newState := oldState.termios 47 | newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF 48 | newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG 49 | if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { 50 | return nil, err 51 | } 52 | 53 | return &oldState, nil 54 | } 55 | 56 | // GetState returns the current state of a terminal which may be useful to 57 | // restore the terminal after a signal. 58 | func GetState(fd int) (*State, error) { 59 | var oldState State 60 | if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { 61 | return nil, err 62 | } 63 | 64 | return &oldState, nil 65 | } 66 | 67 | // Restore restores the terminal connected to the given file descriptor to a 68 | // previous state. 69 | func Restore(fd int, state *State) error { 70 | _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) 71 | return err 72 | } 73 | 74 | // GetSize returns the dimensions of the given terminal. 75 | func GetSize(fd int) (width, height int, err error) { 76 | var dimensions [4]uint16 77 | 78 | if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 { 79 | return -1, -1, err 80 | } 81 | return int(dimensions[1]), int(dimensions[0]), nil 82 | } 83 | 84 | // ReadPassword reads a line of input from a terminal without local echo. This 85 | // is commonly used for inputting passwords and other sensitive data. The slice 86 | // returned does not include the \n. 87 | func ReadPassword(fd int) ([]byte, error) { 88 | var oldState syscall.Termios 89 | if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); err != 0 { 90 | return nil, err 91 | } 92 | 93 | newState := oldState 94 | newState.Lflag &^= syscall.ECHO 95 | newState.Lflag |= syscall.ICANON | syscall.ISIG 96 | newState.Iflag |= syscall.ICRNL 97 | if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { 98 | return nil, err 99 | } 100 | 101 | defer func() { 102 | syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0) 103 | }() 104 | 105 | var buf [16]byte 106 | var ret []byte 107 | for { 108 | n, err := syscall.Read(fd, buf[:]) 109 | if err != nil { 110 | return nil, err 111 | } 112 | if n == 0 { 113 | if len(ret) == 0 { 114 | return nil, io.EOF 115 | } 116 | break 117 | } 118 | if buf[n-1] == '\n' { 119 | n-- 120 | } 121 | ret = append(ret, buf[:n]...) 122 | if n < len(buf) { 123 | break 124 | } 125 | } 126 | 127 | return ret, nil 128 | } 129 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/agent/keyring.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package agent 6 | 7 | import ( 8 | "bytes" 9 | "crypto/rand" 10 | "crypto/subtle" 11 | "errors" 12 | "fmt" 13 | "sync" 14 | 15 | "github.com/JFrogDev/artifactory-cli-go/Godeps/_workspace/src/golang.org/x/crypto/ssh" 16 | ) 17 | 18 | type privKey struct { 19 | signer ssh.Signer 20 | comment string 21 | } 22 | 23 | type keyring struct { 24 | mu sync.Mutex 25 | keys []privKey 26 | 27 | locked bool 28 | passphrase []byte 29 | } 30 | 31 | var errLocked = errors.New("agent: locked") 32 | 33 | // NewKeyring returns an Agent that holds keys in memory. It is safe 34 | // for concurrent use by multiple goroutines. 35 | func NewKeyring() Agent { 36 | return &keyring{} 37 | } 38 | 39 | // RemoveAll removes all identities. 40 | func (r *keyring) RemoveAll() error { 41 | r.mu.Lock() 42 | defer r.mu.Unlock() 43 | if r.locked { 44 | return errLocked 45 | } 46 | 47 | r.keys = nil 48 | return nil 49 | } 50 | 51 | // Remove removes all identities with the given public key. 52 | func (r *keyring) Remove(key ssh.PublicKey) error { 53 | r.mu.Lock() 54 | defer r.mu.Unlock() 55 | if r.locked { 56 | return errLocked 57 | } 58 | 59 | want := key.Marshal() 60 | found := false 61 | for i := 0; i < len(r.keys); { 62 | if bytes.Equal(r.keys[i].signer.PublicKey().Marshal(), want) { 63 | found = true 64 | r.keys[i] = r.keys[len(r.keys)-1] 65 | r.keys = r.keys[:len(r.keys)-1] 66 | continue 67 | } else { 68 | i++ 69 | } 70 | } 71 | 72 | if !found { 73 | return errors.New("agent: key not found") 74 | } 75 | return nil 76 | } 77 | 78 | // Lock locks the agent. Sign and Remove will fail, and List will empty an empty list. 79 | func (r *keyring) Lock(passphrase []byte) error { 80 | r.mu.Lock() 81 | defer r.mu.Unlock() 82 | if r.locked { 83 | return errLocked 84 | } 85 | 86 | r.locked = true 87 | r.passphrase = passphrase 88 | return nil 89 | } 90 | 91 | // Unlock undoes the effect of Lock 92 | func (r *keyring) Unlock(passphrase []byte) error { 93 | r.mu.Lock() 94 | defer r.mu.Unlock() 95 | if !r.locked { 96 | return errors.New("agent: not locked") 97 | } 98 | if len(passphrase) != len(r.passphrase) || 1 != subtle.ConstantTimeCompare(passphrase, r.passphrase) { 99 | return fmt.Errorf("agent: incorrect passphrase") 100 | } 101 | 102 | r.locked = false 103 | r.passphrase = nil 104 | return nil 105 | } 106 | 107 | // List returns the identities known to the agent. 108 | func (r *keyring) List() ([]*Key, error) { 109 | r.mu.Lock() 110 | defer r.mu.Unlock() 111 | if r.locked { 112 | // section 2.7: locked agents return empty. 113 | return nil, nil 114 | } 115 | 116 | var ids []*Key 117 | for _, k := range r.keys { 118 | pub := k.signer.PublicKey() 119 | ids = append(ids, &Key{ 120 | Format: pub.Type(), 121 | Blob: pub.Marshal(), 122 | Comment: k.comment}) 123 | } 124 | return ids, nil 125 | } 126 | 127 | // Insert adds a private key to the keyring. If a certificate 128 | // is given, that certificate is added as public key. Note that 129 | // any constraints given are ignored. 130 | func (r *keyring) Add(key AddedKey) error { 131 | r.mu.Lock() 132 | defer r.mu.Unlock() 133 | if r.locked { 134 | return errLocked 135 | } 136 | signer, err := ssh.NewSignerFromKey(key.PrivateKey) 137 | 138 | if err != nil { 139 | return err 140 | } 141 | 142 | if cert := key.Certificate; cert != nil { 143 | signer, err = ssh.NewCertSigner(cert, signer) 144 | if err != nil { 145 | return err 146 | } 147 | } 148 | 149 | r.keys = append(r.keys, privKey{signer, key.Comment}) 150 | 151 | return nil 152 | } 153 | 154 | // Sign returns a signature for the data. 155 | func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { 156 | r.mu.Lock() 157 | defer r.mu.Unlock() 158 | if r.locked { 159 | return nil, errLocked 160 | } 161 | 162 | wanted := key.Marshal() 163 | for _, k := range r.keys { 164 | if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) { 165 | return k.signer.Sign(rand.Reader, data) 166 | } 167 | } 168 | return nil, errors.New("not found") 169 | } 170 | 171 | // Signers returns signers for all the known keys. 172 | func (r *keyring) Signers() ([]ssh.Signer, error) { 173 | r.mu.Lock() 174 | defer r.mu.Unlock() 175 | if r.locked { 176 | return nil, errLocked 177 | } 178 | 179 | s := make([]ssh.Signer, 0, len(r.keys)) 180 | for _, k := range r.keys { 181 | s = append(s, k.signer) 182 | } 183 | return s, nil 184 | } 185 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/terminal/util_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | // Package terminal provides support functions for dealing with terminals, as 8 | // commonly found on UNIX systems. 9 | // 10 | // Putting a terminal into raw mode is the most common requirement: 11 | // 12 | // oldState, err := terminal.MakeRaw(0) 13 | // if err != nil { 14 | // panic(err) 15 | // } 16 | // defer terminal.Restore(0, oldState) 17 | package terminal 18 | 19 | import ( 20 | "io" 21 | "syscall" 22 | "unsafe" 23 | ) 24 | 25 | const ( 26 | enableLineInput = 2 27 | enableEchoInput = 4 28 | enableProcessedInput = 1 29 | enableWindowInput = 8 30 | enableMouseInput = 16 31 | enableInsertMode = 32 32 | enableQuickEditMode = 64 33 | enableExtendedFlags = 128 34 | enableAutoPosition = 256 35 | enableProcessedOutput = 1 36 | enableWrapAtEolOutput = 2 37 | ) 38 | 39 | var kernel32 = syscall.NewLazyDLL("kernel32.dll") 40 | 41 | var ( 42 | procGetConsoleMode = kernel32.NewProc("GetConsoleMode") 43 | procSetConsoleMode = kernel32.NewProc("SetConsoleMode") 44 | procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") 45 | ) 46 | 47 | type ( 48 | short int16 49 | word uint16 50 | 51 | coord struct { 52 | x short 53 | y short 54 | } 55 | smallRect struct { 56 | left short 57 | top short 58 | right short 59 | bottom short 60 | } 61 | consoleScreenBufferInfo struct { 62 | size coord 63 | cursorPosition coord 64 | attributes word 65 | window smallRect 66 | maximumWindowSize coord 67 | } 68 | ) 69 | 70 | type State struct { 71 | mode uint32 72 | } 73 | 74 | // IsTerminal returns true if the given file descriptor is a terminal. 75 | func IsTerminal(fd int) bool { 76 | var st uint32 77 | r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) 78 | return r != 0 && e == 0 79 | } 80 | 81 | // MakeRaw put the terminal connected to the given file descriptor into raw 82 | // mode and returns the previous state of the terminal so that it can be 83 | // restored. 84 | func MakeRaw(fd int) (*State, error) { 85 | var st uint32 86 | _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) 87 | if e != 0 { 88 | return nil, error(e) 89 | } 90 | st &^= (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput) 91 | _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0) 92 | if e != 0 { 93 | return nil, error(e) 94 | } 95 | return &State{st}, nil 96 | } 97 | 98 | // GetState returns the current state of a terminal which may be useful to 99 | // restore the terminal after a signal. 100 | func GetState(fd int) (*State, error) { 101 | var st uint32 102 | _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) 103 | if e != 0 { 104 | return nil, error(e) 105 | } 106 | return &State{st}, nil 107 | } 108 | 109 | // Restore restores the terminal connected to the given file descriptor to a 110 | // previous state. 111 | func Restore(fd int, state *State) error { 112 | _, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0) 113 | return err 114 | } 115 | 116 | // GetSize returns the dimensions of the given terminal. 117 | func GetSize(fd int) (width, height int, err error) { 118 | var info consoleScreenBufferInfo 119 | _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0) 120 | if e != 0 { 121 | return 0, 0, error(e) 122 | } 123 | return int(info.size.x), int(info.size.y), nil 124 | } 125 | 126 | // ReadPassword reads a line of input from a terminal without local echo. This 127 | // is commonly used for inputting passwords and other sensitive data. The slice 128 | // returned does not include the \n. 129 | func ReadPassword(fd int) ([]byte, error) { 130 | var st uint32 131 | _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) 132 | if e != 0 { 133 | return nil, error(e) 134 | } 135 | old := st 136 | 137 | st &^= (enableEchoInput) 138 | st |= (enableProcessedInput | enableLineInput | enableProcessedOutput) 139 | _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0) 140 | if e != 0 { 141 | return nil, error(e) 142 | } 143 | 144 | defer func() { 145 | syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0) 146 | }() 147 | 148 | var buf [16]byte 149 | var ret []byte 150 | for { 151 | n, err := syscall.Read(syscall.Handle(fd), buf[:]) 152 | if err != nil { 153 | return nil, err 154 | } 155 | if n == 0 { 156 | if len(ret) == 0 { 157 | return nil, io.EOF 158 | } 159 | break 160 | } 161 | if buf[n-1] == '\n' { 162 | n-- 163 | } 164 | if n > 0 && buf[n-1] == '\r' { 165 | n-- 166 | } 167 | ret = append(ret, buf[:n]...) 168 | if n < len(buf) { 169 | break 170 | } 171 | } 172 | 173 | return ret, nil 174 | } 175 | -------------------------------------------------------------------------------- /utils/aqlQueryBuilder.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | func BuildAqlSearchQuery(searchPattern string, recursive bool, props string) string { 8 | searchPattern = prepareSearchPattern(searchPattern) 9 | index := strings.Index(searchPattern, "/") 10 | 11 | repo := searchPattern[:index] 12 | searchPattern = searchPattern[index+1:] 13 | 14 | pairs := createPathFilePairs(searchPattern, recursive) 15 | size := len(pairs) 16 | 17 | json := 18 | "{" + 19 | "\"repo\": \"" + repo + "\"," + 20 | buildPropsQuery(props) + 21 | "\"$or\": [" 22 | 23 | if size == 0 { 24 | json += 25 | "{" + 26 | buildInnerQuery(repo, ".", searchPattern) + 27 | "}" 28 | } else { 29 | for i := 0; i < size; i++ { 30 | json += 31 | "{" + 32 | buildInnerQuery(repo, pairs[i].path, pairs[i].file) + 33 | "}" 34 | 35 | if (i+1 < size) { 36 | json += "," 37 | } 38 | } 39 | } 40 | 41 | json += 42 | "]" + 43 | "}" 44 | 45 | return "items.find(" + json + ")" 46 | } 47 | 48 | func prepareSearchPattern(pattern string) string { 49 | index := strings.Index(pattern, "/") 50 | if index < 0 { 51 | pattern += "/" 52 | } 53 | if strings.HasSuffix(pattern, "/") { 54 | pattern += "*" 55 | } 56 | return pattern 57 | } 58 | 59 | func buildPropsQuery(props string) string { 60 | if props == "" { 61 | return "" 62 | } 63 | propList := strings.Split(props, ";") 64 | query := "" 65 | for _, prop := range propList { 66 | keyVal := strings.Split(prop, "=") 67 | if len(keyVal) != 2 { 68 | Exit(ExitCodeError, "Invalid props pattern: " + props) 69 | } 70 | key := keyVal[0] 71 | value := keyVal[1] 72 | query += 73 | "\"@" + key + "\": {\"$match\" : \"" + value + "\"}," 74 | } 75 | return query 76 | } 77 | 78 | func buildInnerQuery(repo string, path string, name string) string { 79 | query := 80 | "\"$and\": [{" + 81 | "\"path\": {" + 82 | "\"$match\":" + "\"" + path + "\"" + 83 | "}," + 84 | "\"name\":{" + 85 | "\"$match\":" + "\"" + name + "\"" + 86 | "}" + 87 | "}]" 88 | 89 | return query 90 | } 91 | 92 | // We need to translate the provided download pattern to an AQL query. 93 | // In Artifactory, for each artifact the name and path of the artifact are saved separately. 94 | // We therefore need to build an AQL query that covers all possible paths and names the provided 95 | // pattern can include. 96 | // For example, the pattern a/* can include the two following files: 97 | // a/file1.tgz and also a/b/file2.tgz 98 | // To achieve that, this function parses the pattern by splitting it by its * characters. 99 | // The end result is a list of PathFilePair structs. 100 | // Each struct represent a possible path and file name pair to be included in AQL query with an "or" relationship. 101 | func createPathFilePairs(pattern string, recursive bool) []PathFilePair { 102 | pairs := []PathFilePair{} 103 | if (pattern == "*") { 104 | if recursive { 105 | pairs = append(pairs, PathFilePair{"*", "*"}) 106 | } else { 107 | pairs = append(pairs, PathFilePair{".", "*"}) 108 | } 109 | return pairs 110 | } 111 | 112 | index := strings.LastIndex(pattern, "/") 113 | var path string 114 | var name string 115 | if index < 0 { 116 | pairs = append(pairs, PathFilePair{".", pattern}) 117 | path = "" 118 | name = pattern 119 | } else { 120 | path = pattern[0:index] 121 | name = pattern[index+1:] 122 | pairs = append(pairs, PathFilePair{path, name}) 123 | } 124 | if !recursive { 125 | return pairs 126 | } 127 | if name == "*" { 128 | path += "/*" 129 | pairs = append(pairs, PathFilePair{path, "*"}) 130 | return pairs 131 | } 132 | pattern = name 133 | 134 | sections := strings.Split(pattern, "*") 135 | size := len(sections) 136 | 137 | for i := 0; i < size; i++ { 138 | if sections[i] == "" { 139 | continue 140 | } 141 | options := []string{} 142 | if i > 0 { 143 | options = append(options, "/" + sections[i]) 144 | } 145 | if i+1 < size { 146 | options = append(options, sections[i] + "/") 147 | } 148 | for _, option := range options { 149 | str := "" 150 | for j := 0; j < size; j++ { 151 | if (j > 0) { 152 | str += "*" 153 | } 154 | if (j == i) { 155 | str += option 156 | } else { 157 | str += sections[j] 158 | } 159 | } 160 | split := strings.Split(str, "/") 161 | filePath := split[0] 162 | fileName := split[1] 163 | if fileName == "" { 164 | fileName = "*" 165 | } 166 | pairs = append(pairs, PathFilePair{path + filePath, fileName}) 167 | } 168 | } 169 | return pairs 170 | } 171 | 172 | type PathFilePair struct { 173 | path string 174 | file string 175 | } -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/agent/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package agent 6 | 7 | import ( 8 | "crypto/rsa" 9 | "encoding/binary" 10 | "fmt" 11 | "io" 12 | "log" 13 | "math/big" 14 | 15 | "github.com/JFrogDev/artifactory-cli-go/Godeps/_workspace/src/golang.org/x/crypto/ssh" 16 | ) 17 | 18 | // Server wraps an Agent and uses it to implement the agent side of 19 | // the SSH-agent, wire protocol. 20 | type server struct { 21 | agent Agent 22 | } 23 | 24 | func (s *server) processRequestBytes(reqData []byte) []byte { 25 | rep, err := s.processRequest(reqData) 26 | if err != nil { 27 | if err != errLocked { 28 | // TODO(hanwen): provide better logging interface? 29 | log.Printf("agent %d: %v", reqData[0], err) 30 | } 31 | return []byte{agentFailure} 32 | } 33 | 34 | if err == nil && rep == nil { 35 | return []byte{agentSuccess} 36 | } 37 | 38 | return ssh.Marshal(rep) 39 | } 40 | 41 | func marshalKey(k *Key) []byte { 42 | var record struct { 43 | Blob []byte 44 | Comment string 45 | } 46 | record.Blob = k.Marshal() 47 | record.Comment = k.Comment 48 | 49 | return ssh.Marshal(&record) 50 | } 51 | 52 | type agentV1IdentityMsg struct { 53 | Numkeys uint32 `sshtype:"2"` 54 | } 55 | 56 | type agentRemoveIdentityMsg struct { 57 | KeyBlob []byte `sshtype:"18"` 58 | } 59 | 60 | type agentLockMsg struct { 61 | Passphrase []byte `sshtype:"22"` 62 | } 63 | 64 | type agentUnlockMsg struct { 65 | Passphrase []byte `sshtype:"23"` 66 | } 67 | 68 | func (s *server) processRequest(data []byte) (interface{}, error) { 69 | switch data[0] { 70 | case agentRequestV1Identities: 71 | return &agentV1IdentityMsg{0}, nil 72 | case agentRemoveIdentity: 73 | var req agentRemoveIdentityMsg 74 | if err := ssh.Unmarshal(data, &req); err != nil { 75 | return nil, err 76 | } 77 | 78 | var wk wireKey 79 | if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil { 80 | return nil, err 81 | } 82 | 83 | return nil, s.agent.Remove(&Key{Format: wk.Format, Blob: req.KeyBlob}) 84 | 85 | case agentRemoveAllIdentities: 86 | return nil, s.agent.RemoveAll() 87 | 88 | case agentLock: 89 | var req agentLockMsg 90 | if err := ssh.Unmarshal(data, &req); err != nil { 91 | return nil, err 92 | } 93 | 94 | return nil, s.agent.Lock(req.Passphrase) 95 | 96 | case agentUnlock: 97 | var req agentLockMsg 98 | if err := ssh.Unmarshal(data, &req); err != nil { 99 | return nil, err 100 | } 101 | return nil, s.agent.Unlock(req.Passphrase) 102 | 103 | case agentSignRequest: 104 | var req signRequestAgentMsg 105 | if err := ssh.Unmarshal(data, &req); err != nil { 106 | return nil, err 107 | } 108 | 109 | var wk wireKey 110 | if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil { 111 | return nil, err 112 | } 113 | 114 | k := &Key{ 115 | Format: wk.Format, 116 | Blob: req.KeyBlob, 117 | } 118 | 119 | sig, err := s.agent.Sign(k, req.Data) // TODO(hanwen): flags. 120 | if err != nil { 121 | return nil, err 122 | } 123 | return &signResponseAgentMsg{SigBlob: ssh.Marshal(sig)}, nil 124 | case agentRequestIdentities: 125 | keys, err := s.agent.List() 126 | if err != nil { 127 | return nil, err 128 | } 129 | 130 | rep := identitiesAnswerAgentMsg{ 131 | NumKeys: uint32(len(keys)), 132 | } 133 | for _, k := range keys { 134 | rep.Keys = append(rep.Keys, marshalKey(k)...) 135 | } 136 | return rep, nil 137 | case agentAddIdentity: 138 | return nil, s.insertIdentity(data) 139 | } 140 | 141 | return nil, fmt.Errorf("unknown opcode %d", data[0]) 142 | } 143 | 144 | func (s *server) insertIdentity(req []byte) error { 145 | var record struct { 146 | Type string `sshtype:"17"` 147 | Rest []byte `ssh:"rest"` 148 | } 149 | if err := ssh.Unmarshal(req, &record); err != nil { 150 | return err 151 | } 152 | 153 | switch record.Type { 154 | case ssh.KeyAlgoRSA: 155 | var k rsaKeyMsg 156 | if err := ssh.Unmarshal(req, &k); err != nil { 157 | return err 158 | } 159 | 160 | priv := rsa.PrivateKey{ 161 | PublicKey: rsa.PublicKey{ 162 | E: int(k.E.Int64()), 163 | N: k.N, 164 | }, 165 | D: k.D, 166 | Primes: []*big.Int{k.P, k.Q}, 167 | } 168 | priv.Precompute() 169 | 170 | return s.agent.Add(AddedKey{PrivateKey: &priv, Comment: k.Comments}) 171 | } 172 | return fmt.Errorf("not implemented: %s", record.Type) 173 | } 174 | 175 | // ServeAgent serves the agent protocol on the given connection. It 176 | // returns when an I/O error occurs. 177 | func ServeAgent(agent Agent, c io.ReadWriter) error { 178 | s := &server{agent} 179 | 180 | var length [4]byte 181 | for { 182 | if _, err := io.ReadFull(c, length[:]); err != nil { 183 | return err 184 | } 185 | l := binary.BigEndian.Uint32(length[:]) 186 | if l > maxAgentResponseBytes { 187 | // We also cap requests. 188 | return fmt.Errorf("agent: request too large: %d", l) 189 | } 190 | 191 | req := make([]byte, l) 192 | if _, err := io.ReadFull(c, req); err != nil { 193 | return err 194 | } 195 | 196 | repData := s.processRequestBytes(req) 197 | if len(repData) > maxAgentResponseBytes { 198 | return fmt.Errorf("agent: reply too large: %d bytes", len(repData)) 199 | } 200 | 201 | binary.BigEndian.PutUint32(length[:], uint32(len(repData))) 202 | if _, err := c.Write(length[:]); err != nil { 203 | return err 204 | } 205 | if _, err := c.Write(repData); err != nil { 206 | return err 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/command.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "strings" 7 | ) 8 | 9 | // Command is a subcommand for a cli.App. 10 | type Command struct { 11 | // The name of the command 12 | Name string 13 | // short name of the command. Typically one character (deprecated, use `Aliases`) 14 | ShortName string 15 | // A list of aliases for the command 16 | Aliases []string 17 | // A short description of the usage of this command 18 | Usage string 19 | // A longer explanation of how the command works 20 | Description string 21 | // A short description of the arguments of this command 22 | ArgsUsage string 23 | // The function to call when checking for bash command completions 24 | BashComplete func(context *Context) 25 | // An action to execute before any sub-subcommands are run, but after the context is ready 26 | // If a non-nil error is returned, no sub-subcommands are run 27 | Before func(context *Context) error 28 | // An action to execute after any subcommands are run, but after the subcommand has finished 29 | // It is run even if Action() panics 30 | After func(context *Context) error 31 | // The function to call when this command is invoked 32 | Action func(context *Context) 33 | // List of child commands 34 | Subcommands []Command 35 | // List of flags to parse 36 | Flags []Flag 37 | // Treat all flags as normal arguments if true 38 | SkipFlagParsing bool 39 | // Boolean to hide built-in help command 40 | HideHelp bool 41 | 42 | // Full name of command for help, defaults to full command name, including parent commands. 43 | HelpName string 44 | commandNamePath []string 45 | } 46 | 47 | // Returns the full name of the command. 48 | // For subcommands this ensures that parent commands are part of the command path 49 | func (c Command) FullName() string { 50 | if c.commandNamePath == nil { 51 | return c.Name 52 | } 53 | return strings.Join(c.commandNamePath, " ") 54 | } 55 | 56 | // Invokes the command given the context, parses ctx.Args() to generate command-specific flags 57 | func (c Command) Run(ctx *Context) error { 58 | if len(c.Subcommands) > 0 || c.Before != nil || c.After != nil { 59 | return c.startApp(ctx) 60 | } 61 | 62 | if !c.HideHelp && (HelpFlag != BoolFlag{}) { 63 | // append help to flags 64 | c.Flags = append( 65 | c.Flags, 66 | HelpFlag, 67 | ) 68 | } 69 | 70 | if ctx.App.EnableBashCompletion { 71 | c.Flags = append(c.Flags, BashCompletionFlag) 72 | } 73 | 74 | set := flagSet(c.Name, c.Flags) 75 | set.SetOutput(ioutil.Discard) 76 | 77 | var err error 78 | if !c.SkipFlagParsing { 79 | firstFlagIndex := -1 80 | terminatorIndex := -1 81 | for index, arg := range ctx.Args() { 82 | if arg == "--" { 83 | terminatorIndex = index 84 | break 85 | } else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 { 86 | firstFlagIndex = index 87 | } 88 | } 89 | 90 | if firstFlagIndex > -1 { 91 | args := ctx.Args() 92 | regularArgs := make([]string, len(args[1:firstFlagIndex])) 93 | copy(regularArgs, args[1:firstFlagIndex]) 94 | 95 | var flagArgs []string 96 | if terminatorIndex > -1 { 97 | flagArgs = args[firstFlagIndex:terminatorIndex] 98 | regularArgs = append(regularArgs, args[terminatorIndex:]...) 99 | } else { 100 | flagArgs = args[firstFlagIndex:] 101 | } 102 | 103 | err = set.Parse(append(flagArgs, regularArgs...)) 104 | } else { 105 | err = set.Parse(ctx.Args().Tail()) 106 | } 107 | } else { 108 | if c.SkipFlagParsing { 109 | err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...)) 110 | } 111 | } 112 | 113 | if err != nil { 114 | fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.") 115 | fmt.Fprintln(ctx.App.Writer) 116 | ShowCommandHelp(ctx, c.Name) 117 | return err 118 | } 119 | 120 | nerr := normalizeFlags(c.Flags, set) 121 | if nerr != nil { 122 | fmt.Fprintln(ctx.App.Writer, nerr) 123 | fmt.Fprintln(ctx.App.Writer) 124 | ShowCommandHelp(ctx, c.Name) 125 | return nerr 126 | } 127 | context := NewContext(ctx.App, set, ctx) 128 | 129 | if checkCommandCompletions(context, c.Name) { 130 | return nil 131 | } 132 | 133 | if checkCommandHelp(context, c.Name) { 134 | return nil 135 | } 136 | context.Command = c 137 | c.Action(context) 138 | return nil 139 | } 140 | 141 | func (c Command) Names() []string { 142 | names := []string{c.Name} 143 | 144 | if c.ShortName != "" { 145 | names = append(names, c.ShortName) 146 | } 147 | 148 | return append(names, c.Aliases...) 149 | } 150 | 151 | // Returns true if Command.Name or Command.ShortName matches given name 152 | func (c Command) HasName(name string) bool { 153 | for _, n := range c.Names() { 154 | if n == name { 155 | return true 156 | } 157 | } 158 | return false 159 | } 160 | 161 | func (c Command) startApp(ctx *Context) error { 162 | app := NewApp() 163 | 164 | // set the name and usage 165 | app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) 166 | if c.HelpName == "" { 167 | app.HelpName = c.HelpName 168 | } else { 169 | app.HelpName = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) 170 | } 171 | 172 | if c.Description != "" { 173 | app.Usage = c.Description 174 | } else { 175 | app.Usage = c.Usage 176 | } 177 | 178 | // set CommandNotFound 179 | app.CommandNotFound = ctx.App.CommandNotFound 180 | 181 | // set the flags and commands 182 | app.Commands = c.Subcommands 183 | app.Flags = c.Flags 184 | app.HideHelp = c.HideHelp 185 | 186 | app.Version = ctx.App.Version 187 | app.HideVersion = ctx.App.HideVersion 188 | app.Compiled = ctx.App.Compiled 189 | app.Author = ctx.App.Author 190 | app.Email = ctx.App.Email 191 | app.Writer = ctx.App.Writer 192 | 193 | // bash completion 194 | app.EnableBashCompletion = ctx.App.EnableBashCompletion 195 | if c.BashComplete != nil { 196 | app.BashComplete = c.BashComplete 197 | } 198 | 199 | // set the actions 200 | app.Before = c.Before 201 | app.After = c.After 202 | if c.Action != nil { 203 | app.Action = c.Action 204 | } else { 205 | app.Action = helpSubcommand.Action 206 | } 207 | 208 | var newCmds []Command 209 | for _, cc := range app.Commands { 210 | cc.commandNamePath = []string{c.Name, cc.Name} 211 | newCmds = append(newCmds, cc) 212 | } 213 | app.Commands = newCmds 214 | 215 | return app.RunAsSubcommand(ctx) 216 | } 217 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/curve25519/mont25519_amd64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build amd64,!gccgo,!appengine 6 | 7 | package curve25519 8 | 9 | // These functions are implemented in the .s files. The names of the functions 10 | // in the rest of the file are also taken from the SUPERCOP sources to help 11 | // people following along. 12 | 13 | //go:noescape 14 | 15 | func cswap(inout *[5]uint64, v uint64) 16 | 17 | //go:noescape 18 | 19 | func ladderstep(inout *[5][5]uint64) 20 | 21 | //go:noescape 22 | 23 | func freeze(inout *[5]uint64) 24 | 25 | //go:noescape 26 | 27 | func mul(dest, a, b *[5]uint64) 28 | 29 | //go:noescape 30 | 31 | func square(out, in *[5]uint64) 32 | 33 | // mladder uses a Montgomery ladder to calculate (xr/zr) *= s. 34 | func mladder(xr, zr *[5]uint64, s *[32]byte) { 35 | var work [5][5]uint64 36 | 37 | work[0] = *xr 38 | setint(&work[1], 1) 39 | setint(&work[2], 0) 40 | work[3] = *xr 41 | setint(&work[4], 1) 42 | 43 | j := uint(6) 44 | var prevbit byte 45 | 46 | for i := 31; i >= 0; i-- { 47 | for j < 8 { 48 | bit := ((*s)[i] >> j) & 1 49 | swap := bit ^ prevbit 50 | prevbit = bit 51 | cswap(&work[1], uint64(swap)) 52 | ladderstep(&work) 53 | j-- 54 | } 55 | j = 7 56 | } 57 | 58 | *xr = work[1] 59 | *zr = work[2] 60 | } 61 | 62 | func scalarMult(out, in, base *[32]byte) { 63 | var e [32]byte 64 | copy(e[:], (*in)[:]) 65 | e[0] &= 248 66 | e[31] &= 127 67 | e[31] |= 64 68 | 69 | var t, z [5]uint64 70 | unpack(&t, base) 71 | mladder(&t, &z, &e) 72 | invert(&z, &z) 73 | mul(&t, &t, &z) 74 | pack(out, &t) 75 | } 76 | 77 | func setint(r *[5]uint64, v uint64) { 78 | r[0] = v 79 | r[1] = 0 80 | r[2] = 0 81 | r[3] = 0 82 | r[4] = 0 83 | } 84 | 85 | // unpack sets r = x where r consists of 5, 51-bit limbs in little-endian 86 | // order. 87 | func unpack(r *[5]uint64, x *[32]byte) { 88 | r[0] = uint64(x[0]) | 89 | uint64(x[1])<<8 | 90 | uint64(x[2])<<16 | 91 | uint64(x[3])<<24 | 92 | uint64(x[4])<<32 | 93 | uint64(x[5])<<40 | 94 | uint64(x[6]&7)<<48 95 | 96 | r[1] = uint64(x[6])>>3 | 97 | uint64(x[7])<<5 | 98 | uint64(x[8])<<13 | 99 | uint64(x[9])<<21 | 100 | uint64(x[10])<<29 | 101 | uint64(x[11])<<37 | 102 | uint64(x[12]&63)<<45 103 | 104 | r[2] = uint64(x[12])>>6 | 105 | uint64(x[13])<<2 | 106 | uint64(x[14])<<10 | 107 | uint64(x[15])<<18 | 108 | uint64(x[16])<<26 | 109 | uint64(x[17])<<34 | 110 | uint64(x[18])<<42 | 111 | uint64(x[19]&1)<<50 112 | 113 | r[3] = uint64(x[19])>>1 | 114 | uint64(x[20])<<7 | 115 | uint64(x[21])<<15 | 116 | uint64(x[22])<<23 | 117 | uint64(x[23])<<31 | 118 | uint64(x[24])<<39 | 119 | uint64(x[25]&15)<<47 120 | 121 | r[4] = uint64(x[25])>>4 | 122 | uint64(x[26])<<4 | 123 | uint64(x[27])<<12 | 124 | uint64(x[28])<<20 | 125 | uint64(x[29])<<28 | 126 | uint64(x[30])<<36 | 127 | uint64(x[31]&127)<<44 128 | } 129 | 130 | // pack sets out = x where out is the usual, little-endian form of the 5, 131 | // 51-bit limbs in x. 132 | func pack(out *[32]byte, x *[5]uint64) { 133 | t := *x 134 | freeze(&t) 135 | 136 | out[0] = byte(t[0]) 137 | out[1] = byte(t[0] >> 8) 138 | out[2] = byte(t[0] >> 16) 139 | out[3] = byte(t[0] >> 24) 140 | out[4] = byte(t[0] >> 32) 141 | out[5] = byte(t[0] >> 40) 142 | out[6] = byte(t[0] >> 48) 143 | 144 | out[6] ^= byte(t[1]<<3) & 0xf8 145 | out[7] = byte(t[1] >> 5) 146 | out[8] = byte(t[1] >> 13) 147 | out[9] = byte(t[1] >> 21) 148 | out[10] = byte(t[1] >> 29) 149 | out[11] = byte(t[1] >> 37) 150 | out[12] = byte(t[1] >> 45) 151 | 152 | out[12] ^= byte(t[2]<<6) & 0xc0 153 | out[13] = byte(t[2] >> 2) 154 | out[14] = byte(t[2] >> 10) 155 | out[15] = byte(t[2] >> 18) 156 | out[16] = byte(t[2] >> 26) 157 | out[17] = byte(t[2] >> 34) 158 | out[18] = byte(t[2] >> 42) 159 | out[19] = byte(t[2] >> 50) 160 | 161 | out[19] ^= byte(t[3]<<1) & 0xfe 162 | out[20] = byte(t[3] >> 7) 163 | out[21] = byte(t[3] >> 15) 164 | out[22] = byte(t[3] >> 23) 165 | out[23] = byte(t[3] >> 31) 166 | out[24] = byte(t[3] >> 39) 167 | out[25] = byte(t[3] >> 47) 168 | 169 | out[25] ^= byte(t[4]<<4) & 0xf0 170 | out[26] = byte(t[4] >> 4) 171 | out[27] = byte(t[4] >> 12) 172 | out[28] = byte(t[4] >> 20) 173 | out[29] = byte(t[4] >> 28) 174 | out[30] = byte(t[4] >> 36) 175 | out[31] = byte(t[4] >> 44) 176 | } 177 | 178 | // invert calculates r = x^-1 mod p using Fermat's little theorem. 179 | func invert(r *[5]uint64, x *[5]uint64) { 180 | var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64 181 | 182 | square(&z2, x) /* 2 */ 183 | square(&t, &z2) /* 4 */ 184 | square(&t, &t) /* 8 */ 185 | mul(&z9, &t, x) /* 9 */ 186 | mul(&z11, &z9, &z2) /* 11 */ 187 | square(&t, &z11) /* 22 */ 188 | mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */ 189 | 190 | square(&t, &z2_5_0) /* 2^6 - 2^1 */ 191 | for i := 1; i < 5; i++ { /* 2^20 - 2^10 */ 192 | square(&t, &t) 193 | } 194 | mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */ 195 | 196 | square(&t, &z2_10_0) /* 2^11 - 2^1 */ 197 | for i := 1; i < 10; i++ { /* 2^20 - 2^10 */ 198 | square(&t, &t) 199 | } 200 | mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */ 201 | 202 | square(&t, &z2_20_0) /* 2^21 - 2^1 */ 203 | for i := 1; i < 20; i++ { /* 2^40 - 2^20 */ 204 | square(&t, &t) 205 | } 206 | mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */ 207 | 208 | square(&t, &t) /* 2^41 - 2^1 */ 209 | for i := 1; i < 10; i++ { /* 2^50 - 2^10 */ 210 | square(&t, &t) 211 | } 212 | mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */ 213 | 214 | square(&t, &z2_50_0) /* 2^51 - 2^1 */ 215 | for i := 1; i < 50; i++ { /* 2^100 - 2^50 */ 216 | square(&t, &t) 217 | } 218 | mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */ 219 | 220 | square(&t, &z2_100_0) /* 2^101 - 2^1 */ 221 | for i := 1; i < 100; i++ { /* 2^200 - 2^100 */ 222 | square(&t, &t) 223 | } 224 | mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */ 225 | 226 | square(&t, &t) /* 2^201 - 2^1 */ 227 | for i := 1; i < 50; i++ { /* 2^250 - 2^50 */ 228 | square(&t, &t) 229 | } 230 | mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */ 231 | 232 | square(&t, &t) /* 2^251 - 2^1 */ 233 | square(&t, &t) /* 2^252 - 2^2 */ 234 | square(&t, &t) /* 2^253 - 2^3 */ 235 | 236 | square(&t, &t) /* 2^254 - 2^4 */ 237 | 238 | square(&t, &t) /* 2^255 - 2^5 */ 239 | mul(r, &t, &z11) /* 2^255 - 21 */ 240 | } 241 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/help.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | "text/tabwriter" 8 | "text/template" 9 | ) 10 | 11 | // The text template for the Default help topic. 12 | // cli.go uses text/template to render templates. You can 13 | // render custom help text by setting this variable. 14 | var AppHelpTemplate = `NAME: 15 | {{.Name}} - {{.Usage}} 16 | 17 | USAGE: 18 | {{.HelpName}} {{if .Flags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} 19 | {{if .Version}} 20 | VERSION: 21 | {{.Version}} 22 | {{end}}{{if len .Authors}} 23 | AUTHOR(S): 24 | {{range .Authors}}{{ . }}{{end}} 25 | {{end}}{{if .Commands}} 26 | COMMANDS: 27 | {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} 28 | {{end}}{{end}}{{if .Flags}} 29 | GLOBAL OPTIONS: 30 | {{range .Flags}}{{.}} 31 | {{end}}{{end}}{{if .Copyright }} 32 | COPYRIGHT: 33 | {{.Copyright}} 34 | {{end}} 35 | ` 36 | 37 | // The text template for the command help topic. 38 | // cli.go uses text/template to render templates. You can 39 | // render custom help text by setting this variable. 40 | var CommandHelpTemplate = `NAME: 41 | {{.HelpName}} - {{.Usage}} 42 | 43 | USAGE: 44 | {{.HelpName}}{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Description}} 45 | 46 | DESCRIPTION: 47 | {{.Description}}{{end}}{{if .Flags}} 48 | 49 | OPTIONS: 50 | {{range .Flags}}{{.}} 51 | {{end}}{{ end }} 52 | ` 53 | 54 | // The text template for the subcommand help topic. 55 | // cli.go uses text/template to render templates. You can 56 | // render custom help text by setting this variable. 57 | var SubcommandHelpTemplate = `NAME: 58 | {{.HelpName}} - {{.Usage}} 59 | 60 | USAGE: 61 | {{.HelpName}} command{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} 62 | 63 | COMMANDS: 64 | {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} 65 | {{end}}{{if .Flags}} 66 | OPTIONS: 67 | {{range .Flags}}{{.}} 68 | {{end}}{{end}} 69 | ` 70 | 71 | var helpCommand = Command{ 72 | Name: "help", 73 | Aliases: []string{"h"}, 74 | Usage: "Shows a list of commands or help for one command", 75 | ArgsUsage: "[command]", 76 | Action: func(c *Context) { 77 | args := c.Args() 78 | if args.Present() { 79 | ShowCommandHelp(c, args.First()) 80 | } else { 81 | ShowAppHelp(c) 82 | } 83 | }, 84 | } 85 | 86 | var helpSubcommand = Command{ 87 | Name: "help", 88 | Aliases: []string{"h"}, 89 | Usage: "Shows a list of commands or help for one command", 90 | ArgsUsage: "[command]", 91 | Action: func(c *Context) { 92 | args := c.Args() 93 | if args.Present() { 94 | ShowCommandHelp(c, args.First()) 95 | } else { 96 | ShowSubcommandHelp(c) 97 | } 98 | }, 99 | } 100 | 101 | // Prints help for the App or Command 102 | type helpPrinter func(w io.Writer, templ string, data interface{}) 103 | 104 | var HelpPrinter helpPrinter = printHelp 105 | 106 | // Prints version for the App 107 | var VersionPrinter = printVersion 108 | 109 | func ShowAppHelp(c *Context) { 110 | HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) 111 | } 112 | 113 | // Prints the list of subcommands as the default app completion method 114 | func DefaultAppComplete(c *Context) { 115 | for _, command := range c.App.Commands { 116 | for _, name := range command.Names() { 117 | fmt.Fprintln(c.App.Writer, name) 118 | } 119 | } 120 | } 121 | 122 | // Prints help for the given command 123 | func ShowCommandHelp(ctx *Context, command string) { 124 | // show the subcommand help for a command with subcommands 125 | if command == "" { 126 | HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) 127 | return 128 | } 129 | 130 | for _, c := range ctx.App.Commands { 131 | if c.HasName(command) { 132 | HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) 133 | return 134 | } 135 | } 136 | 137 | if ctx.App.CommandNotFound != nil { 138 | ctx.App.CommandNotFound(ctx, command) 139 | } else { 140 | fmt.Fprintf(ctx.App.Writer, "No help topic for '%v'\n", command) 141 | } 142 | } 143 | 144 | // Prints help for the given subcommand 145 | func ShowSubcommandHelp(c *Context) { 146 | ShowCommandHelp(c, c.Command.Name) 147 | } 148 | 149 | // Prints the version number of the App 150 | func ShowVersion(c *Context) { 151 | VersionPrinter(c) 152 | } 153 | 154 | func printVersion(c *Context) { 155 | fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) 156 | } 157 | 158 | // Prints the lists of commands within a given context 159 | func ShowCompletions(c *Context) { 160 | a := c.App 161 | if a != nil && a.BashComplete != nil { 162 | a.BashComplete(c) 163 | } 164 | } 165 | 166 | // Prints the custom completions for a given command 167 | func ShowCommandCompletions(ctx *Context, command string) { 168 | c := ctx.App.Command(command) 169 | if c != nil && c.BashComplete != nil { 170 | c.BashComplete(ctx) 171 | } 172 | } 173 | 174 | func printHelp(out io.Writer, templ string, data interface{}) { 175 | funcMap := template.FuncMap{ 176 | "join": strings.Join, 177 | } 178 | 179 | w := tabwriter.NewWriter(out, 0, 8, 1, '\t', 0) 180 | t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) 181 | err := t.Execute(w, data) 182 | if err != nil { 183 | panic(err) 184 | } 185 | w.Flush() 186 | } 187 | 188 | func checkVersion(c *Context) bool { 189 | found := false 190 | if VersionFlag.Name != "" { 191 | eachName(VersionFlag.Name, func(name string) { 192 | if c.GlobalBool(name) || c.Bool(name) { 193 | found = true 194 | } 195 | }) 196 | } 197 | return found 198 | } 199 | 200 | func checkHelp(c *Context) bool { 201 | found := false 202 | if HelpFlag.Name != "" { 203 | eachName(HelpFlag.Name, func(name string) { 204 | if c.GlobalBool(name) || c.Bool(name) { 205 | found = true 206 | } 207 | }) 208 | } 209 | return found 210 | } 211 | 212 | func checkCommandHelp(c *Context, name string) bool { 213 | if c.Bool("h") || c.Bool("help") { 214 | ShowCommandHelp(c, name) 215 | return true 216 | } 217 | 218 | return false 219 | } 220 | 221 | func checkSubcommandHelp(c *Context) bool { 222 | if c.GlobalBool("h") || c.GlobalBool("help") { 223 | ShowSubcommandHelp(c) 224 | return true 225 | } 226 | 227 | return false 228 | } 229 | 230 | func checkCompletions(c *Context) bool { 231 | if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion { 232 | ShowCompletions(c) 233 | return true 234 | } 235 | 236 | return false 237 | } 238 | 239 | func checkCommandCompletions(c *Context, name string) bool { 240 | if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { 241 | ShowCommandCompletions(c, name) 242 | return true 243 | } 244 | 245 | return false 246 | } 247 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ssh 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "net" 11 | "sync" 12 | ) 13 | 14 | // Client implements a traditional SSH client that supports shells, 15 | // subprocesses, port forwarding and tunneled dialing. 16 | type Client struct { 17 | Conn 18 | 19 | forwards forwardList // forwarded tcpip connections from the remote side 20 | mu sync.Mutex 21 | channelHandlers map[string]chan NewChannel 22 | } 23 | 24 | // HandleChannelOpen returns a channel on which NewChannel requests 25 | // for the given type are sent. If the type already is being handled, 26 | // nil is returned. The channel is closed when the connection is closed. 27 | func (c *Client) HandleChannelOpen(channelType string) <-chan NewChannel { 28 | c.mu.Lock() 29 | defer c.mu.Unlock() 30 | if c.channelHandlers == nil { 31 | // The SSH channel has been closed. 32 | c := make(chan NewChannel) 33 | close(c) 34 | return c 35 | } 36 | 37 | ch := c.channelHandlers[channelType] 38 | if ch != nil { 39 | return nil 40 | } 41 | 42 | ch = make(chan NewChannel, 16) 43 | c.channelHandlers[channelType] = ch 44 | return ch 45 | } 46 | 47 | // NewClient creates a Client on top of the given connection. 48 | func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client { 49 | conn := &Client{ 50 | Conn: c, 51 | channelHandlers: make(map[string]chan NewChannel, 1), 52 | } 53 | 54 | go conn.handleGlobalRequests(reqs) 55 | go conn.handleChannelOpens(chans) 56 | go func() { 57 | conn.Wait() 58 | conn.forwards.closeAll() 59 | }() 60 | go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-tcpip")) 61 | return conn 62 | } 63 | 64 | // NewClientConn establishes an authenticated SSH connection using c 65 | // as the underlying transport. The Request and NewChannel channels 66 | // must be serviced or the connection will hang. 67 | func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) { 68 | fullConf := *config 69 | fullConf.SetDefaults() 70 | conn := &connection{ 71 | sshConn: sshConn{conn: c}, 72 | } 73 | 74 | if err := conn.clientHandshake(addr, &fullConf); err != nil { 75 | c.Close() 76 | return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %v", err) 77 | } 78 | conn.mux = newMux(conn.transport) 79 | return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil 80 | } 81 | 82 | // clientHandshake performs the client side key exchange. See RFC 4253 Section 83 | // 7. 84 | func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) error { 85 | if config.ClientVersion != "" { 86 | c.clientVersion = []byte(config.ClientVersion) 87 | } else { 88 | c.clientVersion = []byte(packageVersion) 89 | } 90 | var err error 91 | c.serverVersion, err = exchangeVersions(c.sshConn.conn, c.clientVersion) 92 | if err != nil { 93 | return err 94 | } 95 | 96 | c.transport = newClientTransport( 97 | newTransport(c.sshConn.conn, config.Rand, true /* is client */), 98 | c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr()) 99 | if err := c.transport.requestKeyChange(); err != nil { 100 | return err 101 | } 102 | 103 | if packet, err := c.transport.readPacket(); err != nil { 104 | return err 105 | } else if packet[0] != msgNewKeys { 106 | return unexpectedMessageError(msgNewKeys, packet[0]) 107 | } 108 | 109 | // We just did the key change, so the session ID is established. 110 | c.sessionID = c.transport.getSessionID() 111 | 112 | return c.clientAuthenticate(config) 113 | } 114 | 115 | // verifyHostKeySignature verifies the host key obtained in the key 116 | // exchange. 117 | func verifyHostKeySignature(hostKey PublicKey, result *kexResult) error { 118 | sig, rest, ok := parseSignatureBody(result.Signature) 119 | if len(rest) > 0 || !ok { 120 | return errors.New("ssh: signature parse error") 121 | } 122 | 123 | return hostKey.Verify(result.H, sig) 124 | } 125 | 126 | // NewSession opens a new Session for this client. (A session is a remote 127 | // execution of a program.) 128 | func (c *Client) NewSession() (*Session, error) { 129 | ch, in, err := c.OpenChannel("session", nil) 130 | if err != nil { 131 | return nil, err 132 | } 133 | return newSession(ch, in) 134 | } 135 | 136 | func (c *Client) handleGlobalRequests(incoming <-chan *Request) { 137 | for r := range incoming { 138 | // This handles keepalive messages and matches 139 | // the behaviour of OpenSSH. 140 | r.Reply(false, nil) 141 | } 142 | } 143 | 144 | // handleChannelOpens channel open messages from the remote side. 145 | func (c *Client) handleChannelOpens(in <-chan NewChannel) { 146 | for ch := range in { 147 | c.mu.Lock() 148 | handler := c.channelHandlers[ch.ChannelType()] 149 | c.mu.Unlock() 150 | 151 | if handler != nil { 152 | handler <- ch 153 | } else { 154 | ch.Reject(UnknownChannelType, fmt.Sprintf("unknown channel type: %v", ch.ChannelType())) 155 | } 156 | } 157 | 158 | c.mu.Lock() 159 | for _, ch := range c.channelHandlers { 160 | close(ch) 161 | } 162 | c.channelHandlers = nil 163 | c.mu.Unlock() 164 | } 165 | 166 | // Dial starts a client connection to the given SSH server. It is a 167 | // convenience function that connects to the given network address, 168 | // initiates the SSH handshake, and then sets up a Client. For access 169 | // to incoming channels and requests, use net.Dial with NewClientConn 170 | // instead. 171 | func Dial(network, addr string, config *ClientConfig) (*Client, error) { 172 | conn, err := net.Dial(network, addr) 173 | if err != nil { 174 | return nil, err 175 | } 176 | c, chans, reqs, err := NewClientConn(conn, addr, config) 177 | if err != nil { 178 | return nil, err 179 | } 180 | return NewClient(c, chans, reqs), nil 181 | } 182 | 183 | // A ClientConfig structure is used to configure a Client. It must not be 184 | // modified after having been passed to an SSH function. 185 | type ClientConfig struct { 186 | // Config contains configuration that is shared between clients and 187 | // servers. 188 | Config 189 | 190 | // User contains the username to authenticate as. 191 | User string 192 | 193 | // Auth contains possible authentication methods to use with the 194 | // server. Only the first instance of a particular RFC 4252 method will 195 | // be used during authentication. 196 | Auth []AuthMethod 197 | 198 | // HostKeyCallback, if not nil, is called during the cryptographic 199 | // handshake to validate the server's host key. A nil HostKeyCallback 200 | // implies that all host keys are accepted. 201 | HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error 202 | 203 | // ClientVersion contains the version identification string that will 204 | // be used for the connection. If empty, a reasonable default is used. 205 | ClientVersion string 206 | 207 | // HostKeyAlgorithms lists the key types that the client will 208 | // accept from the server as host key, in order of 209 | // preference. If empty, a reasonable default is used. Any 210 | // string returned from PublicKey.Type method may be used, or 211 | // any of the CertAlgoXxxx and KeyAlgoXxxx constants. 212 | HostKeyAlgorithms []string 213 | } 214 | -------------------------------------------------------------------------------- /utils/ioUtils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "bytes" 7 | "strings" 8 | "bufio" 9 | "strconv" 10 | "net/http" 11 | "io/ioutil" 12 | "path/filepath" 13 | ) 14 | 15 | var tempDirPath string 16 | 17 | func GetFileNameFromPath(path string) string { 18 | index := strings.LastIndex(path, "/") 19 | if index != -1 { 20 | return path[index+1:] 21 | } 22 | index = strings.LastIndex(path, "\\") 23 | if index != -1 { 24 | return path[index+1:] 25 | } 26 | return path 27 | } 28 | 29 | func IsDir(path string) bool { 30 | if !IsPathExists(path) { 31 | return false 32 | } 33 | f, err := os.Stat(path) 34 | CheckError(err) 35 | return f.IsDir() 36 | } 37 | 38 | func IsPathExists(path string) bool { 39 | _, err := os.Stat(path) 40 | return !os.IsNotExist(err) 41 | } 42 | 43 | func IsFileExists(path string) bool { 44 | if !IsPathExists(path) { 45 | return false 46 | } 47 | f, err := os.Stat(path) 48 | CheckError(err) 49 | return !f.IsDir() 50 | } 51 | 52 | func IsDirExists(path string) bool { 53 | if !IsPathExists(path) { 54 | return false 55 | } 56 | f, err := os.Stat(path) 57 | CheckError(err) 58 | return f.IsDir() 59 | } 60 | 61 | func ReadFile(filePath string) []byte { 62 | content, err := ioutil.ReadFile(filePath) 63 | CheckError(err) 64 | return content 65 | } 66 | 67 | func UploadFile(f *os.File, url string, artifactoryDetails ArtifactoryDetails, details *FileDetails) *http.Response { 68 | if details == nil { 69 | details = GetFileDetails(f.Name()) 70 | } 71 | req, err := http.NewRequest("PUT", url, f) 72 | CheckError(err) 73 | req.ContentLength = details.Size 74 | req.Close = true 75 | 76 | addAuthHeaders(req, artifactoryDetails) 77 | addUserAgentHeader(req) 78 | 79 | size := strconv.FormatInt(details.Size, 10) 80 | 81 | req.Header.Set("Content-Length", size) 82 | req.Header.Set("X-Checksum-Sha1", details.Sha1) 83 | req.Header.Set("X-Checksum-Md5", details.Md5) 84 | 85 | client := &http.Client{} 86 | resp, err := client.Do(req) 87 | CheckError(err) 88 | defer resp.Body.Close() 89 | return resp 90 | } 91 | 92 | func DownloadFile(downloadPath, localPath, fileName string, flat bool, artifactoryDetails ArtifactoryDetails) *http.Response { 93 | if !flat && localPath != "" { 94 | os.MkdirAll(localPath ,0777) 95 | fileName = localPath + "/" + fileName 96 | } 97 | 98 | out, err := os.Create(fileName) 99 | CheckError(err) 100 | defer out.Close() 101 | resp, body := SendGet(downloadPath, nil, artifactoryDetails) 102 | out.Write(body) 103 | CheckError(err) 104 | return resp 105 | } 106 | 107 | func SendPut(url string, content []byte, headers map[string]string, artifactoryDetails ArtifactoryDetails) (*http.Response, []byte) { 108 | return Send("PUT", url, content, headers, artifactoryDetails) 109 | } 110 | 111 | func SendPost(url string, content []byte, artifactoryDetails ArtifactoryDetails) (*http.Response, []byte) { 112 | return Send("POST", url, content, nil, artifactoryDetails) 113 | } 114 | 115 | func SendGet(url string, headers map[string]string, artifactoryDetails ArtifactoryDetails) (*http.Response, []byte) { 116 | return Send("GET", url, nil, headers, artifactoryDetails) 117 | } 118 | 119 | func SendHead(url string, artifactoryDetails ArtifactoryDetails) (*http.Response, []byte) { 120 | return Send("HEAD", url, nil, nil, artifactoryDetails) 121 | } 122 | 123 | func Send(method string, url string, content []byte, headers map[string]string, 124 | artifactoryDetails ArtifactoryDetails) (*http.Response, []byte) { 125 | 126 | var req *http.Request 127 | var err error 128 | 129 | if content != nil { 130 | req, err = http.NewRequest(method, url, bytes.NewBuffer(content)) 131 | } else { 132 | req, err = http.NewRequest(method, url, nil) 133 | } 134 | CheckError(err) 135 | req.Close = true 136 | 137 | addAuthHeaders(req, artifactoryDetails) 138 | addUserAgentHeader(req) 139 | if headers != nil { 140 | for name := range headers { 141 | req.Header.Set(name, headers[name]) 142 | } 143 | } 144 | 145 | client := &http.Client{} 146 | resp, err := client.Do(req) 147 | CheckError(err) 148 | defer resp.Body.Close() 149 | body, _ := ioutil.ReadAll(resp.Body) 150 | return resp, body 151 | } 152 | 153 | // Return the recursive list of files and directories in the specified path 154 | func ListFilesRecursive(path string) []string { 155 | fileList := []string{} 156 | err := filepath.Walk(path, func(path string, f os.FileInfo, err error) error { 157 | fileList = append(fileList, path) 158 | return nil 159 | }) 160 | CheckError(err) 161 | return fileList 162 | } 163 | 164 | // Return the list of files and directories in the specified path 165 | func ListFiles(path string) []string { 166 | sep := GetFileSeperator() 167 | if !strings.HasSuffix(path, sep) { 168 | path += sep 169 | } 170 | fileList := []string{} 171 | files, _ := ioutil.ReadDir(path) 172 | 173 | for _, f := range files { 174 | filePath := path + f.Name() 175 | if IsFileExists(filePath) { 176 | fileList = append(fileList, filePath) 177 | } 178 | } 179 | return fileList 180 | } 181 | 182 | func GetTempDirPath() string { 183 | if tempDirPath == "" { 184 | Exit(ExitCodeError, "Function cannot be used before 'tempDirPath' is created.") 185 | } 186 | return tempDirPath 187 | } 188 | 189 | func CreateTempDirPath() { 190 | if tempDirPath != "" { 191 | Exit(ExitCodeError, "'tempDirPath' has already been initialized.") 192 | } 193 | path, err := ioutil.TempDir("", "artifactory.cli.") 194 | CheckError(err) 195 | tempDirPath = path 196 | } 197 | 198 | func RemoveTempDir() { 199 | if IsDirExists(tempDirPath) { 200 | os.RemoveAll(tempDirPath) 201 | } 202 | } 203 | 204 | // Reads the content of the file in the source path and appends it to 205 | // the file in the destination path. 206 | func AppendFile(srcPath string, destFile *os.File) { 207 | srcFile, err := os.Open(srcPath) 208 | CheckError(err) 209 | 210 | defer func() { 211 | err := srcFile.Close(); 212 | CheckError(err) 213 | }() 214 | 215 | reader := bufio.NewReader(srcFile) 216 | 217 | writer := bufio.NewWriter(destFile) 218 | buf := make([]byte, 1024000) 219 | for { 220 | n, err := reader.Read(buf) 221 | if err != io.EOF { 222 | CheckError(err) 223 | } 224 | if n == 0 { 225 | break 226 | } 227 | _, err = writer.Write(buf[:n]) 228 | CheckError(err) 229 | } 230 | err = writer.Flush() 231 | CheckError(err) 232 | } 233 | 234 | func addAuthHeaders(req *http.Request, artifactoryDetails ArtifactoryDetails) { 235 | if artifactoryDetails.SshAuthHeaders != nil { 236 | for name := range artifactoryDetails.SshAuthHeaders { 237 | req.Header.Set(name, artifactoryDetails.SshAuthHeaders[name]) 238 | } 239 | } else 240 | if artifactoryDetails.User != "" && artifactoryDetails.Password != "" { 241 | req.SetBasicAuth(artifactoryDetails.User, artifactoryDetails.Password) 242 | } 243 | } 244 | 245 | func addUserAgentHeader(req *http.Request) { 246 | req.Header.Set("User-Agent", "artifactory-cli-go/" + GetVersion()) 247 | } -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/app.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | "path" 9 | "time" 10 | ) 11 | 12 | // App is the main structure of a cli application. It is recomended that 13 | // an app be created with the cli.NewApp() function 14 | type App struct { 15 | // The name of the program. Defaults to path.Base(os.Args[0]) 16 | Name string 17 | // Full name of command for help, defaults to Name 18 | HelpName string 19 | // Description of the program. 20 | Usage string 21 | // Description of the program argument format. 22 | ArgsUsage string 23 | // Version of the program 24 | Version string 25 | // List of commands to execute 26 | Commands []Command 27 | // List of flags to parse 28 | Flags []Flag 29 | // Boolean to enable bash completion commands 30 | EnableBashCompletion bool 31 | // Boolean to hide built-in help command 32 | HideHelp bool 33 | // Boolean to hide built-in version flag 34 | HideVersion bool 35 | // An action to execute when the bash-completion flag is set 36 | BashComplete func(context *Context) 37 | // An action to execute before any subcommands are run, but after the context is ready 38 | // If a non-nil error is returned, no subcommands are run 39 | Before func(context *Context) error 40 | // An action to execute after any subcommands are run, but after the subcommand has finished 41 | // It is run even if Action() panics 42 | After func(context *Context) error 43 | // The action to execute when no subcommands are specified 44 | Action func(context *Context) 45 | // Execute this function if the proper command cannot be found 46 | CommandNotFound func(context *Context, command string) 47 | // Compilation date 48 | Compiled time.Time 49 | // List of all authors who contributed 50 | Authors []Author 51 | // Copyright of the binary if any 52 | Copyright string 53 | // Name of Author (Note: Use App.Authors, this is deprecated) 54 | Author string 55 | // Email of Author (Note: Use App.Authors, this is deprecated) 56 | Email string 57 | // Writer writer to write output to 58 | Writer io.Writer 59 | } 60 | 61 | // Tries to find out when this binary was compiled. 62 | // Returns the current time if it fails to find it. 63 | func compileTime() time.Time { 64 | info, err := os.Stat(os.Args[0]) 65 | if err != nil { 66 | return time.Now() 67 | } 68 | return info.ModTime() 69 | } 70 | 71 | // Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action. 72 | func NewApp() *App { 73 | return &App{ 74 | Name: path.Base(os.Args[0]), 75 | HelpName: path.Base(os.Args[0]), 76 | Usage: "A new cli application", 77 | Version: "0.0.0", 78 | BashComplete: DefaultAppComplete, 79 | Action: helpCommand.Action, 80 | Compiled: compileTime(), 81 | Writer: os.Stdout, 82 | } 83 | } 84 | 85 | // Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination 86 | func (a *App) Run(arguments []string) (err error) { 87 | if a.Author != "" || a.Email != "" { 88 | a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email}) 89 | } 90 | 91 | newCmds := []Command{} 92 | for _, c := range a.Commands { 93 | if c.HelpName == "" { 94 | c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) 95 | } 96 | newCmds = append(newCmds, c) 97 | } 98 | a.Commands = newCmds 99 | 100 | // append help to commands 101 | if a.Command(helpCommand.Name) == nil && !a.HideHelp { 102 | a.Commands = append(a.Commands, helpCommand) 103 | if (HelpFlag != BoolFlag{}) { 104 | a.appendFlag(HelpFlag) 105 | } 106 | } 107 | 108 | //append version/help flags 109 | if a.EnableBashCompletion { 110 | a.appendFlag(BashCompletionFlag) 111 | } 112 | 113 | if !a.HideVersion { 114 | a.appendFlag(VersionFlag) 115 | } 116 | 117 | // parse flags 118 | set := flagSet(a.Name, a.Flags) 119 | set.SetOutput(ioutil.Discard) 120 | err = set.Parse(arguments[1:]) 121 | nerr := normalizeFlags(a.Flags, set) 122 | if nerr != nil { 123 | fmt.Fprintln(a.Writer, nerr) 124 | context := NewContext(a, set, nil) 125 | ShowAppHelp(context) 126 | return nerr 127 | } 128 | context := NewContext(a, set, nil) 129 | 130 | if checkCompletions(context) { 131 | return nil 132 | } 133 | 134 | if err != nil { 135 | fmt.Fprintln(a.Writer, "Incorrect Usage.") 136 | fmt.Fprintln(a.Writer) 137 | ShowAppHelp(context) 138 | return err 139 | } 140 | 141 | if !a.HideHelp && checkHelp(context) { 142 | ShowAppHelp(context) 143 | return nil 144 | } 145 | 146 | if !a.HideVersion && checkVersion(context) { 147 | ShowVersion(context) 148 | return nil 149 | } 150 | 151 | if a.After != nil { 152 | defer func() { 153 | afterErr := a.After(context) 154 | if afterErr != nil { 155 | if err != nil { 156 | err = NewMultiError(err, afterErr) 157 | } else { 158 | err = afterErr 159 | } 160 | } 161 | }() 162 | } 163 | 164 | if a.Before != nil { 165 | err := a.Before(context) 166 | if err != nil { 167 | return err 168 | } 169 | } 170 | 171 | args := context.Args() 172 | if args.Present() { 173 | name := args.First() 174 | c := a.Command(name) 175 | if c != nil { 176 | return c.Run(context) 177 | } 178 | } 179 | 180 | // Run default Action 181 | a.Action(context) 182 | return nil 183 | } 184 | 185 | // Another entry point to the cli app, takes care of passing arguments and error handling 186 | func (a *App) RunAndExitOnError() { 187 | if err := a.Run(os.Args); err != nil { 188 | fmt.Fprintln(os.Stderr, err) 189 | os.Exit(1) 190 | } 191 | } 192 | 193 | // Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags 194 | func (a *App) RunAsSubcommand(ctx *Context) (err error) { 195 | // append help to commands 196 | if len(a.Commands) > 0 { 197 | if a.Command(helpCommand.Name) == nil && !a.HideHelp { 198 | a.Commands = append(a.Commands, helpCommand) 199 | if (HelpFlag != BoolFlag{}) { 200 | a.appendFlag(HelpFlag) 201 | } 202 | } 203 | } 204 | 205 | newCmds := []Command{} 206 | for _, c := range a.Commands { 207 | if c.HelpName == "" { 208 | c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) 209 | } 210 | newCmds = append(newCmds, c) 211 | } 212 | a.Commands = newCmds 213 | 214 | // append flags 215 | if a.EnableBashCompletion { 216 | a.appendFlag(BashCompletionFlag) 217 | } 218 | 219 | // parse flags 220 | set := flagSet(a.Name, a.Flags) 221 | set.SetOutput(ioutil.Discard) 222 | err = set.Parse(ctx.Args().Tail()) 223 | nerr := normalizeFlags(a.Flags, set) 224 | context := NewContext(a, set, ctx) 225 | 226 | if nerr != nil { 227 | fmt.Fprintln(a.Writer, nerr) 228 | fmt.Fprintln(a.Writer) 229 | if len(a.Commands) > 0 { 230 | ShowSubcommandHelp(context) 231 | } else { 232 | ShowCommandHelp(ctx, context.Args().First()) 233 | } 234 | return nerr 235 | } 236 | 237 | if checkCompletions(context) { 238 | return nil 239 | } 240 | 241 | if err != nil { 242 | fmt.Fprintln(a.Writer, "Incorrect Usage.") 243 | fmt.Fprintln(a.Writer) 244 | ShowSubcommandHelp(context) 245 | return err 246 | } 247 | 248 | if len(a.Commands) > 0 { 249 | if checkSubcommandHelp(context) { 250 | return nil 251 | } 252 | } else { 253 | if checkCommandHelp(ctx, context.Args().First()) { 254 | return nil 255 | } 256 | } 257 | 258 | if a.After != nil { 259 | defer func() { 260 | afterErr := a.After(context) 261 | if afterErr != nil { 262 | if err != nil { 263 | err = NewMultiError(err, afterErr) 264 | } else { 265 | err = afterErr 266 | } 267 | } 268 | }() 269 | } 270 | 271 | if a.Before != nil { 272 | err := a.Before(context) 273 | if err != nil { 274 | return err 275 | } 276 | } 277 | 278 | args := context.Args() 279 | if args.Present() { 280 | name := args.First() 281 | c := a.Command(name) 282 | if c != nil { 283 | return c.Run(context) 284 | } 285 | } 286 | 287 | // Run default Action 288 | a.Action(context) 289 | 290 | return nil 291 | } 292 | 293 | // Returns the named command on App. Returns nil if the command does not exist 294 | func (a *App) Command(name string) *Command { 295 | for _, c := range a.Commands { 296 | if c.HasName(name) { 297 | return &c 298 | } 299 | } 300 | 301 | return nil 302 | } 303 | 304 | func (a *App) hasFlag(flag Flag) bool { 305 | for _, f := range a.Flags { 306 | if flag == f { 307 | return true 308 | } 309 | } 310 | 311 | return false 312 | } 313 | 314 | func (a *App) appendFlag(flag Flag) { 315 | if !a.hasFlag(flag) { 316 | a.Flags = append(a.Flags, flag) 317 | } 318 | } 319 | 320 | // Author represents someone who has contributed to a cli project. 321 | type Author struct { 322 | Name string // The Authors name 323 | Email string // The Authors email 324 | } 325 | 326 | // String makes Author comply to the Stringer interface, to allow an easy print in the templating process 327 | func (a Author) String() string { 328 | e := "" 329 | if a.Email != "" { 330 | e = "<" + a.Email + "> " 331 | } 332 | 333 | return fmt.Sprintf("%v %v", a.Name, e) 334 | } 335 | -------------------------------------------------------------------------------- /commands/upload.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "os" 5 | "fmt" 6 | "sync" 7 | "strings" 8 | "regexp" 9 | "strconv" 10 | "net/http" 11 | "github.com/JFrogDev/artifactory-cli-go/utils" 12 | ) 13 | 14 | // Uploads the artifacts in the specified local path pattern to the specified target path. 15 | // Returns the total number of artifacts successfully uploaded. 16 | func Upload(localPath, targetPath string, flags *utils.Flags) (totalUploaded, totalFailed int) { 17 | if flags.ArtDetails.SshKeyPath != "" { 18 | utils.SshAuthentication(flags.ArtDetails) 19 | } 20 | 21 | // Get the list of artifacts to be uploaded to Artifactory: 22 | artifacts := getFilesToUpload(localPath, targetPath, flags) 23 | size := len(artifacts) 24 | 25 | // Start a thread for each channel and start uploading: 26 | var wg sync.WaitGroup 27 | 28 | // Create an array of integers, to store the total file that were uploaded successfully. 29 | // Each array item is used by a single thread. 30 | uploadCount := make([]int, flags.Threads, flags.Threads) 31 | 32 | for i := 0; i < flags.Threads; i++ { 33 | wg.Add(1) 34 | go func(threadId int) { 35 | for j := threadId; j < size; j += flags.Threads { 36 | target := flags.ArtDetails.Url + artifacts[j].TargetPath 37 | if uploadFile(artifacts[j].LocalPath, target, flags, utils.GetLogMsgPrefix(threadId, flags.DryRun)) { 38 | uploadCount[threadId]++ 39 | } 40 | } 41 | wg.Done() 42 | }(i) 43 | } 44 | wg.Wait() 45 | totalUploaded = 0 46 | for _, i := range uploadCount { 47 | totalUploaded += i 48 | } 49 | 50 | fmt.Println("Uploaded " + strconv.Itoa(totalUploaded) + " artifacts to Artifactory.") 51 | totalFailed = size-totalUploaded 52 | if totalFailed > 0 { 53 | fmt.Println("Failed uploading " + strconv.Itoa(totalFailed) + " artifacts to Artifactory.") 54 | } 55 | return 56 | } 57 | 58 | func prepareUploadPath(path string) string { 59 | path = strings.Replace(path, "\\", "/", -1) 60 | path = strings.Replace(path, "../", "", -1) 61 | path = strings.Replace(path, "./", "", -1) 62 | return path 63 | } 64 | 65 | func prepareLocalPath(localpath string, useRegExp bool) string { 66 | if localpath == "./" || localpath == ".\\" { 67 | return "^.*$" 68 | } 69 | if strings.HasPrefix(localpath, "./") { 70 | localpath = localpath[2:] 71 | } else 72 | if strings.HasPrefix(localpath, ".\\") { 73 | localpath = localpath[3:] 74 | } 75 | if !useRegExp { 76 | localpath = localPathToRegExp(localpath) 77 | } 78 | return localpath 79 | } 80 | 81 | func localPathToRegExp(localpath string) string { 82 | var wildcard = ".*" 83 | 84 | localpath = strings.Replace(localpath, ".", "\\.", -1) 85 | localpath = strings.Replace(localpath, "*", wildcard, -1) 86 | if strings.HasSuffix(localpath, "/") { 87 | localpath += wildcard 88 | } else 89 | if strings.HasSuffix(localpath, "\\") { 90 | size := len(localpath) 91 | if size > 1 && localpath[size-2 : size-1] != "\\" { 92 | localpath += "\\" 93 | } 94 | localpath += wildcard 95 | } 96 | localpath = "^" + localpath + "$" 97 | return localpath 98 | } 99 | 100 | func getFilesToUpload(localpath string, targetPath string, flags *utils.Flags) []Artifact { 101 | if strings.Index(targetPath, "/") < 0 { 102 | targetPath += "/" 103 | } 104 | rootPath := getRootPath(localpath, flags.UseRegExp) 105 | if !utils.IsPathExists(rootPath) { 106 | utils.Exit(utils.ExitCodeError, "Path does not exist: " + rootPath) 107 | } 108 | localpath = prepareLocalPath(localpath, flags.UseRegExp) 109 | 110 | artifacts := []Artifact{} 111 | // If the path is a single file then return it 112 | if !utils.IsDir(rootPath) { 113 | targetPath := prepareUploadPath(targetPath + rootPath) 114 | artifacts = append(artifacts, Artifact{rootPath, targetPath}) 115 | return artifacts 116 | } 117 | 118 | r, err := regexp.Compile(localpath) 119 | utils.CheckError(err) 120 | 121 | var paths []string 122 | if flags.Recursive { 123 | paths = utils.ListFilesRecursive(rootPath) 124 | } else { 125 | paths = utils.ListFiles(rootPath) 126 | } 127 | 128 | for _, path := range paths { 129 | if utils.IsDir(path) { 130 | continue 131 | } 132 | 133 | groups := r.FindStringSubmatch(path) 134 | size := len(groups) 135 | target := targetPath 136 | if (size > 0) { 137 | for i := 1; i < size; i++ { 138 | group := strings.Replace(groups[i], "\\", "/", -1) 139 | target = strings.Replace(target, "{" + strconv.Itoa(i) + "}", group, -1) 140 | } 141 | if strings.HasSuffix(target, "/") { 142 | if flags.Flat { 143 | target += utils.GetFileNameFromPath(path) 144 | } else { 145 | uploadPath := prepareUploadPath(path) 146 | target += uploadPath 147 | } 148 | } 149 | 150 | artifacts = append(artifacts, Artifact{path, target}) 151 | } 152 | } 153 | return artifacts 154 | } 155 | 156 | // Get the local root path, from which to start collecting artifacts to be uploaded to Artifactory. 157 | func getRootPath(path string, useRegExp bool) string { 158 | // The first step is to split the local path pattern into sections, by the file seperator. 159 | seperator := "/" 160 | sections := strings.Split(path, seperator) 161 | if len(sections) == 1 { 162 | seperator = "\\" 163 | sections = strings.Split(path, seperator) 164 | } 165 | 166 | // Now we start building the root path, making sure to leave out the sub-directory that includes the pattern. 167 | rootPath := "" 168 | for _, section := range sections { 169 | if section == "" { 170 | continue 171 | } 172 | if useRegExp { 173 | if strings.Index(section, "(") != -1 { 174 | break 175 | } 176 | } else { 177 | if strings.Index(section, "*") != -1 { 178 | break 179 | } 180 | } 181 | if rootPath != "" { 182 | rootPath += seperator 183 | } 184 | rootPath += section 185 | } 186 | if len(sections) > 0 && sections[0] == "" { 187 | rootPath = seperator + rootPath 188 | } 189 | if rootPath == "" { 190 | return "." 191 | } 192 | return rootPath 193 | } 194 | 195 | func getDebianProps(debianPropsStr string) (debianProperties string) { 196 | debProps := strings.Split(debianPropsStr, "/") 197 | debianProperties = 198 | ";deb.distribution=" + debProps[0] + 199 | ";deb.component=" + debProps[1] + 200 | ";deb.architecture=" + debProps[2] 201 | 202 | return 203 | } 204 | 205 | // Uploads the file in the specified local path to the specified target path. 206 | // Returns true if the file was successfully uploaded. 207 | func uploadFile(localPath string, targetPath string, flags *utils.Flags, logMsgPrefix string) bool { 208 | if (flags.Props != "") { 209 | targetPath += ";" + flags.Props 210 | } 211 | if flags.Deb != "" { 212 | targetPath += getDebianProps(flags.Deb) 213 | } 214 | 215 | fmt.Println(logMsgPrefix + " Uploading artifact: " + targetPath) 216 | file, err := os.Open(localPath) 217 | utils.CheckError(err) 218 | defer file.Close() 219 | fileInfo, err := file.Stat() 220 | utils.CheckError(err) 221 | 222 | var checksumDeployed bool = false 223 | var resp *http.Response 224 | var details *utils.FileDetails 225 | if fileInfo.Size() >= 10240 { 226 | resp, details = tryChecksumDeploy(localPath, targetPath, flags) 227 | checksumDeployed = !flags.DryRun && (resp.StatusCode == 201 || resp.StatusCode == 200) 228 | } 229 | if !flags.DryRun && !checksumDeployed { 230 | resp = utils.UploadFile(file, targetPath, *flags.ArtDetails, details) 231 | } 232 | if !flags.DryRun { 233 | var strChecksumDeployed string 234 | if checksumDeployed { 235 | strChecksumDeployed = " (Checksum deploy)" 236 | } else { 237 | strChecksumDeployed = "" 238 | } 239 | fmt.Println(logMsgPrefix + " Artifactory response: " + resp.Status + strChecksumDeployed) 240 | } 241 | 242 | return flags.DryRun || checksumDeployed || resp.StatusCode == 201 || resp.StatusCode == 200 243 | } 244 | 245 | func tryChecksumDeploy(filePath, targetPath string, flags *utils.Flags) (*http.Response, *utils.FileDetails) { 246 | details := utils.GetFileDetails(filePath) 247 | headers := make(map[string]string) 248 | headers["X-Checksum-Deploy"] = "true" 249 | headers["X-Checksum-Sha1"] = details.Sha1 250 | headers["X-Checksum-Md5"] = details.Md5 251 | 252 | if flags.DryRun { 253 | return nil, details 254 | } 255 | resp, _ := utils.SendPut(targetPath, nil, headers, *flags.ArtDetails) 256 | return resp, details 257 | } 258 | 259 | type Artifact struct { 260 | LocalPath string 261 | TargetPath string 262 | } -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/mux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ssh 6 | 7 | import ( 8 | "encoding/binary" 9 | "fmt" 10 | "io" 11 | "log" 12 | "sync" 13 | "sync/atomic" 14 | ) 15 | 16 | // debugMux, if set, causes messages in the connection protocol to be 17 | // logged. 18 | const debugMux = false 19 | 20 | // chanList is a thread safe channel list. 21 | type chanList struct { 22 | // protects concurrent access to chans 23 | sync.Mutex 24 | 25 | // chans are indexed by the local id of the channel, which the 26 | // other side should send in the PeersId field. 27 | chans []*channel 28 | 29 | // This is a debugging aid: it offsets all IDs by this 30 | // amount. This helps distinguish otherwise identical 31 | // server/client muxes 32 | offset uint32 33 | } 34 | 35 | // Assigns a channel ID to the given channel. 36 | func (c *chanList) add(ch *channel) uint32 { 37 | c.Lock() 38 | defer c.Unlock() 39 | for i := range c.chans { 40 | if c.chans[i] == nil { 41 | c.chans[i] = ch 42 | return uint32(i) + c.offset 43 | } 44 | } 45 | c.chans = append(c.chans, ch) 46 | return uint32(len(c.chans)-1) + c.offset 47 | } 48 | 49 | // getChan returns the channel for the given ID. 50 | func (c *chanList) getChan(id uint32) *channel { 51 | id -= c.offset 52 | 53 | c.Lock() 54 | defer c.Unlock() 55 | if id < uint32(len(c.chans)) { 56 | return c.chans[id] 57 | } 58 | return nil 59 | } 60 | 61 | func (c *chanList) remove(id uint32) { 62 | id -= c.offset 63 | c.Lock() 64 | if id < uint32(len(c.chans)) { 65 | c.chans[id] = nil 66 | } 67 | c.Unlock() 68 | } 69 | 70 | // dropAll forgets all channels it knows, returning them in a slice. 71 | func (c *chanList) dropAll() []*channel { 72 | c.Lock() 73 | defer c.Unlock() 74 | var r []*channel 75 | 76 | for _, ch := range c.chans { 77 | if ch == nil { 78 | continue 79 | } 80 | r = append(r, ch) 81 | } 82 | c.chans = nil 83 | return r 84 | } 85 | 86 | // mux represents the state for the SSH connection protocol, which 87 | // multiplexes many channels onto a single packet transport. 88 | type mux struct { 89 | conn packetConn 90 | chanList chanList 91 | 92 | incomingChannels chan NewChannel 93 | 94 | globalSentMu sync.Mutex 95 | globalResponses chan interface{} 96 | incomingRequests chan *Request 97 | 98 | errCond *sync.Cond 99 | err error 100 | } 101 | 102 | // When debugging, each new chanList instantiation has a different 103 | // offset. 104 | var globalOff uint32 105 | 106 | func (m *mux) Wait() error { 107 | m.errCond.L.Lock() 108 | defer m.errCond.L.Unlock() 109 | for m.err == nil { 110 | m.errCond.Wait() 111 | } 112 | return m.err 113 | } 114 | 115 | // newMux returns a mux that runs over the given connection. 116 | func newMux(p packetConn) *mux { 117 | m := &mux{ 118 | conn: p, 119 | incomingChannels: make(chan NewChannel, 16), 120 | globalResponses: make(chan interface{}, 1), 121 | incomingRequests: make(chan *Request, 16), 122 | errCond: newCond(), 123 | } 124 | if debugMux { 125 | m.chanList.offset = atomic.AddUint32(&globalOff, 1) 126 | } 127 | 128 | go m.loop() 129 | return m 130 | } 131 | 132 | func (m *mux) sendMessage(msg interface{}) error { 133 | p := Marshal(msg) 134 | return m.conn.writePacket(p) 135 | } 136 | 137 | func (m *mux) SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) { 138 | if wantReply { 139 | m.globalSentMu.Lock() 140 | defer m.globalSentMu.Unlock() 141 | } 142 | 143 | if err := m.sendMessage(globalRequestMsg{ 144 | Type: name, 145 | WantReply: wantReply, 146 | Data: payload, 147 | }); err != nil { 148 | return false, nil, err 149 | } 150 | 151 | if !wantReply { 152 | return false, nil, nil 153 | } 154 | 155 | msg, ok := <-m.globalResponses 156 | if !ok { 157 | return false, nil, io.EOF 158 | } 159 | switch msg := msg.(type) { 160 | case *globalRequestFailureMsg: 161 | return false, msg.Data, nil 162 | case *globalRequestSuccessMsg: 163 | return true, msg.Data, nil 164 | default: 165 | return false, nil, fmt.Errorf("ssh: unexpected response to request: %#v", msg) 166 | } 167 | } 168 | 169 | // ackRequest must be called after processing a global request that 170 | // has WantReply set. 171 | func (m *mux) ackRequest(ok bool, data []byte) error { 172 | if ok { 173 | return m.sendMessage(globalRequestSuccessMsg{Data: data}) 174 | } 175 | return m.sendMessage(globalRequestFailureMsg{Data: data}) 176 | } 177 | 178 | // TODO(hanwen): Disconnect is a transport layer message. We should 179 | // probably send and receive Disconnect somewhere in the transport 180 | // code. 181 | 182 | // Disconnect sends a disconnect message. 183 | func (m *mux) Disconnect(reason uint32, message string) error { 184 | return m.sendMessage(disconnectMsg{ 185 | Reason: reason, 186 | Message: message, 187 | }) 188 | } 189 | 190 | func (m *mux) Close() error { 191 | return m.conn.Close() 192 | } 193 | 194 | // loop runs the connection machine. It will process packets until an 195 | // error is encountered. To synchronize on loop exit, use mux.Wait. 196 | func (m *mux) loop() { 197 | var err error 198 | for err == nil { 199 | err = m.onePacket() 200 | } 201 | 202 | for _, ch := range m.chanList.dropAll() { 203 | ch.close() 204 | } 205 | 206 | close(m.incomingChannels) 207 | close(m.incomingRequests) 208 | close(m.globalResponses) 209 | 210 | m.conn.Close() 211 | 212 | m.errCond.L.Lock() 213 | m.err = err 214 | m.errCond.Broadcast() 215 | m.errCond.L.Unlock() 216 | 217 | if debugMux { 218 | log.Println("loop exit", err) 219 | } 220 | } 221 | 222 | // onePacket reads and processes one packet. 223 | func (m *mux) onePacket() error { 224 | packet, err := m.conn.readPacket() 225 | if err != nil { 226 | return err 227 | } 228 | 229 | if debugMux { 230 | if packet[0] == msgChannelData || packet[0] == msgChannelExtendedData { 231 | log.Printf("decoding(%d): data packet - %d bytes", m.chanList.offset, len(packet)) 232 | } else { 233 | p, _ := decode(packet) 234 | log.Printf("decoding(%d): %d %#v - %d bytes", m.chanList.offset, packet[0], p, len(packet)) 235 | } 236 | } 237 | 238 | switch packet[0] { 239 | case msgNewKeys: 240 | // Ignore notification of key change. 241 | return nil 242 | case msgDisconnect: 243 | return m.handleDisconnect(packet) 244 | case msgChannelOpen: 245 | return m.handleChannelOpen(packet) 246 | case msgGlobalRequest, msgRequestSuccess, msgRequestFailure: 247 | return m.handleGlobalPacket(packet) 248 | } 249 | 250 | // assume a channel packet. 251 | if len(packet) < 5 { 252 | return parseError(packet[0]) 253 | } 254 | id := binary.BigEndian.Uint32(packet[1:]) 255 | ch := m.chanList.getChan(id) 256 | if ch == nil { 257 | return fmt.Errorf("ssh: invalid channel %d", id) 258 | } 259 | 260 | return ch.handlePacket(packet) 261 | } 262 | 263 | func (m *mux) handleDisconnect(packet []byte) error { 264 | var d disconnectMsg 265 | if err := Unmarshal(packet, &d); err != nil { 266 | return err 267 | } 268 | 269 | if debugMux { 270 | log.Printf("caught disconnect: %v", d) 271 | } 272 | return &d 273 | } 274 | 275 | func (m *mux) handleGlobalPacket(packet []byte) error { 276 | msg, err := decode(packet) 277 | if err != nil { 278 | return err 279 | } 280 | 281 | switch msg := msg.(type) { 282 | case *globalRequestMsg: 283 | m.incomingRequests <- &Request{ 284 | Type: msg.Type, 285 | WantReply: msg.WantReply, 286 | Payload: msg.Data, 287 | mux: m, 288 | } 289 | case *globalRequestSuccessMsg, *globalRequestFailureMsg: 290 | m.globalResponses <- msg 291 | default: 292 | panic(fmt.Sprintf("not a global message %#v", msg)) 293 | } 294 | 295 | return nil 296 | } 297 | 298 | // handleChannelOpen schedules a channel to be Accept()ed. 299 | func (m *mux) handleChannelOpen(packet []byte) error { 300 | var msg channelOpenMsg 301 | if err := Unmarshal(packet, &msg); err != nil { 302 | return err 303 | } 304 | 305 | if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { 306 | failMsg := channelOpenFailureMsg{ 307 | PeersId: msg.PeersId, 308 | Reason: ConnectionFailed, 309 | Message: "invalid request", 310 | Language: "en_US.UTF-8", 311 | } 312 | return m.sendMessage(failMsg) 313 | } 314 | 315 | c := m.newChannel(msg.ChanType, channelInbound, msg.TypeSpecificData) 316 | c.remoteId = msg.PeersId 317 | c.maxRemotePayload = msg.MaxPacketSize 318 | c.remoteWin.add(msg.PeersWindow) 319 | m.incomingChannels <- c 320 | return nil 321 | } 322 | 323 | func (m *mux) OpenChannel(chanType string, extra []byte) (Channel, <-chan *Request, error) { 324 | ch, err := m.openChannel(chanType, extra) 325 | if err != nil { 326 | return nil, nil, err 327 | } 328 | 329 | return ch, ch.incomingRequests, nil 330 | } 331 | 332 | func (m *mux) openChannel(chanType string, extra []byte) (*channel, error) { 333 | ch := m.newChannel(chanType, channelOutbound, extra) 334 | 335 | ch.maxIncomingPayload = channelMaxPacket 336 | 337 | open := channelOpenMsg{ 338 | ChanType: chanType, 339 | PeersWindow: ch.myWindow, 340 | MaxPacketSize: ch.maxIncomingPayload, 341 | TypeSpecificData: extra, 342 | PeersId: ch.localId, 343 | } 344 | if err := m.sendMessage(open); err != nil { 345 | return nil, err 346 | } 347 | 348 | switch msg := (<-ch.msg).(type) { 349 | case *channelOpenConfirmMsg: 350 | return ch, nil 351 | case *channelOpenFailureMsg: 352 | return nil, &OpenChannelError{msg.Reason, msg.Message} 353 | default: 354 | return nil, fmt.Errorf("ssh: unexpected packet in response to channel open: %T", msg) 355 | } 356 | } 357 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/transport.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ssh 6 | 7 | import ( 8 | "bufio" 9 | "errors" 10 | "io" 11 | ) 12 | 13 | const ( 14 | gcmCipherID = "aes128-gcm@openssh.com" 15 | aes128cbcID = "aes128-cbc" 16 | ) 17 | 18 | // packetConn represents a transport that implements packet based 19 | // operations. 20 | type packetConn interface { 21 | // Encrypt and send a packet of data to the remote peer. 22 | writePacket(packet []byte) error 23 | 24 | // Read a packet from the connection 25 | readPacket() ([]byte, error) 26 | 27 | // Close closes the write-side of the connection. 28 | Close() error 29 | } 30 | 31 | // transport is the keyingTransport that implements the SSH packet 32 | // protocol. 33 | type transport struct { 34 | reader connectionState 35 | writer connectionState 36 | 37 | bufReader *bufio.Reader 38 | bufWriter *bufio.Writer 39 | rand io.Reader 40 | 41 | io.Closer 42 | 43 | // Initial H used for the session ID. Once assigned this does 44 | // not change, even during subsequent key exchanges. 45 | sessionID []byte 46 | } 47 | 48 | // getSessionID returns the ID of the SSH connection. The return value 49 | // should not be modified. 50 | func (t *transport) getSessionID() []byte { 51 | if t.sessionID == nil { 52 | panic("session ID not set yet") 53 | } 54 | return t.sessionID 55 | } 56 | 57 | // packetCipher represents a combination of SSH encryption/MAC 58 | // protocol. A single instance should be used for one direction only. 59 | type packetCipher interface { 60 | // writePacket encrypts the packet and writes it to w. The 61 | // contents of the packet are generally scrambled. 62 | writePacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error 63 | 64 | // readPacket reads and decrypts a packet of data. The 65 | // returned packet may be overwritten by future calls of 66 | // readPacket. 67 | readPacket(seqnum uint32, r io.Reader) ([]byte, error) 68 | } 69 | 70 | // connectionState represents one side (read or write) of the 71 | // connection. This is necessary because each direction has its own 72 | // keys, and can even have its own algorithms 73 | type connectionState struct { 74 | packetCipher 75 | seqNum uint32 76 | dir direction 77 | pendingKeyChange chan packetCipher 78 | } 79 | 80 | // prepareKeyChange sets up key material for a keychange. The key changes in 81 | // both directions are triggered by reading and writing a msgNewKey packet 82 | // respectively. 83 | func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error { 84 | if t.sessionID == nil { 85 | t.sessionID = kexResult.H 86 | } 87 | 88 | kexResult.SessionID = t.sessionID 89 | 90 | if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil { 91 | return err 92 | } else { 93 | t.reader.pendingKeyChange <- ciph 94 | } 95 | 96 | if ciph, err := newPacketCipher(t.writer.dir, algs.w, kexResult); err != nil { 97 | return err 98 | } else { 99 | t.writer.pendingKeyChange <- ciph 100 | } 101 | 102 | return nil 103 | } 104 | 105 | // Read and decrypt next packet. 106 | func (t *transport) readPacket() ([]byte, error) { 107 | return t.reader.readPacket(t.bufReader) 108 | } 109 | 110 | func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { 111 | packet, err := s.packetCipher.readPacket(s.seqNum, r) 112 | s.seqNum++ 113 | if err == nil && len(packet) == 0 { 114 | err = errors.New("ssh: zero length packet") 115 | } 116 | 117 | if len(packet) > 0 && packet[0] == msgNewKeys { 118 | select { 119 | case cipher := <-s.pendingKeyChange: 120 | s.packetCipher = cipher 121 | default: 122 | return nil, errors.New("ssh: got bogus newkeys message.") 123 | } 124 | } 125 | 126 | // The packet may point to an internal buffer, so copy the 127 | // packet out here. 128 | fresh := make([]byte, len(packet)) 129 | copy(fresh, packet) 130 | 131 | return fresh, err 132 | } 133 | 134 | func (t *transport) writePacket(packet []byte) error { 135 | return t.writer.writePacket(t.bufWriter, t.rand, packet) 136 | } 137 | 138 | func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error { 139 | changeKeys := len(packet) > 0 && packet[0] == msgNewKeys 140 | 141 | err := s.packetCipher.writePacket(s.seqNum, w, rand, packet) 142 | if err != nil { 143 | return err 144 | } 145 | if err = w.Flush(); err != nil { 146 | return err 147 | } 148 | s.seqNum++ 149 | if changeKeys { 150 | select { 151 | case cipher := <-s.pendingKeyChange: 152 | s.packetCipher = cipher 153 | default: 154 | panic("ssh: no key material for msgNewKeys") 155 | } 156 | } 157 | return err 158 | } 159 | 160 | func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport { 161 | t := &transport{ 162 | bufReader: bufio.NewReader(rwc), 163 | bufWriter: bufio.NewWriter(rwc), 164 | rand: rand, 165 | reader: connectionState{ 166 | packetCipher: &streamPacketCipher{cipher: noneCipher{}}, 167 | pendingKeyChange: make(chan packetCipher, 1), 168 | }, 169 | writer: connectionState{ 170 | packetCipher: &streamPacketCipher{cipher: noneCipher{}}, 171 | pendingKeyChange: make(chan packetCipher, 1), 172 | }, 173 | Closer: rwc, 174 | } 175 | if isClient { 176 | t.reader.dir = serverKeys 177 | t.writer.dir = clientKeys 178 | } else { 179 | t.reader.dir = clientKeys 180 | t.writer.dir = serverKeys 181 | } 182 | 183 | return t 184 | } 185 | 186 | type direction struct { 187 | ivTag []byte 188 | keyTag []byte 189 | macKeyTag []byte 190 | } 191 | 192 | var ( 193 | serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}} 194 | clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} 195 | ) 196 | 197 | // generateKeys generates key material for IV, MAC and encryption. 198 | func generateKeys(d direction, algs directionAlgorithms, kex *kexResult) (iv, key, macKey []byte) { 199 | cipherMode := cipherModes[algs.Cipher] 200 | macMode := macModes[algs.MAC] 201 | 202 | iv = make([]byte, cipherMode.ivSize) 203 | key = make([]byte, cipherMode.keySize) 204 | macKey = make([]byte, macMode.keySize) 205 | 206 | generateKeyMaterial(iv, d.ivTag, kex) 207 | generateKeyMaterial(key, d.keyTag, kex) 208 | generateKeyMaterial(macKey, d.macKeyTag, kex) 209 | return 210 | } 211 | 212 | // setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as 213 | // described in RFC 4253, section 6.4. direction should either be serverKeys 214 | // (to setup server->client keys) or clientKeys (for client->server keys). 215 | func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) { 216 | iv, key, macKey := generateKeys(d, algs, kex) 217 | 218 | if algs.Cipher == gcmCipherID { 219 | return newGCMCipher(iv, key, macKey) 220 | } 221 | 222 | if algs.Cipher == aes128cbcID { 223 | return newAESCBCCipher(iv, key, macKey, algs) 224 | } 225 | 226 | c := &streamPacketCipher{ 227 | mac: macModes[algs.MAC].new(macKey), 228 | } 229 | c.macResult = make([]byte, c.mac.Size()) 230 | 231 | var err error 232 | c.cipher, err = cipherModes[algs.Cipher].createStream(key, iv) 233 | if err != nil { 234 | return nil, err 235 | } 236 | 237 | return c, nil 238 | } 239 | 240 | // generateKeyMaterial fills out with key material generated from tag, K, H 241 | // and sessionId, as specified in RFC 4253, section 7.2. 242 | func generateKeyMaterial(out, tag []byte, r *kexResult) { 243 | var digestsSoFar []byte 244 | 245 | h := r.Hash.New() 246 | for len(out) > 0 { 247 | h.Reset() 248 | h.Write(r.K) 249 | h.Write(r.H) 250 | 251 | if len(digestsSoFar) == 0 { 252 | h.Write(tag) 253 | h.Write(r.SessionID) 254 | } else { 255 | h.Write(digestsSoFar) 256 | } 257 | 258 | digest := h.Sum(nil) 259 | n := copy(out, digest) 260 | out = out[n:] 261 | if len(out) > 0 { 262 | digestsSoFar = append(digestsSoFar, digest...) 263 | } 264 | } 265 | } 266 | 267 | const packageVersion = "SSH-2.0-Go" 268 | 269 | // Sends and receives a version line. The versionLine string should 270 | // be US ASCII, start with "SSH-2.0-", and should not include a 271 | // newline. exchangeVersions returns the other side's version line. 272 | func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) { 273 | // Contrary to the RFC, we do not ignore lines that don't 274 | // start with "SSH-2.0-" to make the library usable with 275 | // nonconforming servers. 276 | for _, c := range versionLine { 277 | // The spec disallows non US-ASCII chars, and 278 | // specifically forbids null chars. 279 | if c < 32 { 280 | return nil, errors.New("ssh: junk character in version line") 281 | } 282 | } 283 | if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil { 284 | return 285 | } 286 | 287 | them, err = readVersion(rw) 288 | return them, err 289 | } 290 | 291 | // maxVersionStringBytes is the maximum number of bytes that we'll 292 | // accept as a version string. RFC 4253 section 4.2 limits this at 255 293 | // chars 294 | const maxVersionStringBytes = 255 295 | 296 | // Read version string as specified by RFC 4253, section 4.2. 297 | func readVersion(r io.Reader) ([]byte, error) { 298 | versionString := make([]byte, 0, 64) 299 | var ok bool 300 | var buf [1]byte 301 | 302 | for len(versionString) < maxVersionStringBytes { 303 | _, err := io.ReadFull(r, buf[:]) 304 | if err != nil { 305 | return nil, err 306 | } 307 | // The RFC says that the version should be terminated with \r\n 308 | // but several SSH servers actually only send a \n. 309 | if buf[0] == '\n' { 310 | ok = true 311 | break 312 | } 313 | 314 | // non ASCII chars are disallowed, but we are lenient, 315 | // since Go doesn't use null-terminated strings. 316 | 317 | // The RFC allows a comment after a space, however, 318 | // all of it (version and comments) goes into the 319 | // session hash. 320 | versionString = append(versionString, buf[0]) 321 | } 322 | 323 | if !ok { 324 | return nil, errors.New("ssh: overflow reading version string") 325 | } 326 | 327 | // There might be a '\r' on the end which we should remove. 328 | if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' { 329 | versionString = versionString[:len(versionString)-1] 330 | } 331 | return versionString, nil 332 | } 333 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/README.md: -------------------------------------------------------------------------------- 1 | [![Coverage](http://gocover.io/_badge/github.com/codegangsta/cli?0)](http://gocover.io/github.com/codegangsta/cli) 2 | [![Build Status](https://travis-ci.org/codegangsta/cli.png?branch=master)](https://travis-ci.org/codegangsta/cli) 3 | [![GoDoc](https://godoc.org/github.com/codegangsta/cli?status.svg)](https://godoc.org/github.com/codegangsta/cli) 4 | 5 | # cli.go 6 | `cli.go` is simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way. 7 | 8 | ## Overview 9 | Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app. 10 | 11 | **This is where `cli.go` comes into play.** `cli.go` makes command line programming fun, organized, and expressive! 12 | 13 | ## Installation 14 | Make sure you have a working Go environment (go 1.1+ is *required*). [See the install instructions](http://golang.org/doc/install.html). 15 | 16 | To install `cli.go`, simply run: 17 | ``` 18 | $ go get github.com/codegangsta/cli 19 | ``` 20 | 21 | Make sure your `PATH` includes to the `$GOPATH/bin` directory so your commands can be easily used: 22 | ``` 23 | export PATH=$PATH:$GOPATH/bin 24 | ``` 25 | 26 | ## Getting Started 27 | One of the philosophies behind `cli.go` is that an API should be playful and full of discovery. So a `cli.go` app can be as little as one line of code in `main()`. 28 | 29 | ``` go 30 | package main 31 | 32 | import ( 33 | "os" 34 | "github.com/codegangsta/cli" 35 | ) 36 | 37 | func main() { 38 | cli.NewApp().Run(os.Args) 39 | } 40 | ``` 41 | 42 | This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation: 43 | 44 | ``` go 45 | package main 46 | 47 | import ( 48 | "os" 49 | "github.com/codegangsta/cli" 50 | ) 51 | 52 | func main() { 53 | app := cli.NewApp() 54 | app.Name = "boom" 55 | app.Usage = "make an explosive entrance" 56 | app.Action = func(c *cli.Context) { 57 | println("boom! I say!") 58 | } 59 | 60 | app.Run(os.Args) 61 | } 62 | ``` 63 | 64 | Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below. 65 | 66 | ## Example 67 | 68 | Being a programmer can be a lonely job. Thankfully by the power of automation that is not the case! Let's create a greeter app to fend off our demons of loneliness! 69 | 70 | Start by creating a directory named `greet`, and within it, add a file, `greet.go` with the following code in it: 71 | 72 | ``` go 73 | package main 74 | 75 | import ( 76 | "os" 77 | "github.com/codegangsta/cli" 78 | ) 79 | 80 | func main() { 81 | app := cli.NewApp() 82 | app.Name = "greet" 83 | app.Usage = "fight the loneliness!" 84 | app.Action = func(c *cli.Context) { 85 | println("Hello friend!") 86 | } 87 | 88 | app.Run(os.Args) 89 | } 90 | ``` 91 | 92 | Install our command to the `$GOPATH/bin` directory: 93 | 94 | ``` 95 | $ go install 96 | ``` 97 | 98 | Finally run our new command: 99 | 100 | ``` 101 | $ greet 102 | Hello friend! 103 | ``` 104 | 105 | `cli.go` also generates neat help text: 106 | 107 | ``` 108 | $ greet help 109 | NAME: 110 | greet - fight the loneliness! 111 | 112 | USAGE: 113 | greet [global options] command [command options] [arguments...] 114 | 115 | VERSION: 116 | 0.0.0 117 | 118 | COMMANDS: 119 | help, h Shows a list of commands or help for one command 120 | 121 | GLOBAL OPTIONS 122 | --version Shows version information 123 | ``` 124 | 125 | ### Arguments 126 | You can lookup arguments by calling the `Args` function on `cli.Context`. 127 | 128 | ``` go 129 | ... 130 | app.Action = func(c *cli.Context) { 131 | println("Hello", c.Args()[0]) 132 | } 133 | ... 134 | ``` 135 | 136 | ### Flags 137 | Setting and querying flags is simple. 138 | ``` go 139 | ... 140 | app.Flags = []cli.Flag { 141 | cli.StringFlag{ 142 | Name: "lang", 143 | Value: "english", 144 | Usage: "language for the greeting", 145 | }, 146 | } 147 | app.Action = func(c *cli.Context) { 148 | name := "someone" 149 | if len(c.Args()) > 0 { 150 | name = c.Args()[0] 151 | } 152 | if c.String("lang") == "spanish" { 153 | println("Hola", name) 154 | } else { 155 | println("Hello", name) 156 | } 157 | } 158 | ... 159 | ``` 160 | 161 | You can also set a destination variable for a flag, to which the content will be scanned. 162 | ``` go 163 | ... 164 | var language string 165 | app.Flags = []cli.Flag { 166 | cli.StringFlag{ 167 | Name: "lang", 168 | Value: "english", 169 | Usage: "language for the greeting", 170 | Destination: &language, 171 | }, 172 | } 173 | app.Action = func(c *cli.Context) { 174 | name := "someone" 175 | if len(c.Args()) > 0 { 176 | name = c.Args()[0] 177 | } 178 | if language == "spanish" { 179 | println("Hola", name) 180 | } else { 181 | println("Hello", name) 182 | } 183 | } 184 | ... 185 | ``` 186 | 187 | See full list of flags at http://godoc.org/github.com/codegangsta/cli 188 | 189 | #### Alternate Names 190 | 191 | You can set alternate (or short) names for flags by providing a comma-delimited list for the `Name`. e.g. 192 | 193 | ``` go 194 | app.Flags = []cli.Flag { 195 | cli.StringFlag{ 196 | Name: "lang, l", 197 | Value: "english", 198 | Usage: "language for the greeting", 199 | }, 200 | } 201 | ``` 202 | 203 | That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error. 204 | 205 | #### Values from the Environment 206 | 207 | You can also have the default value set from the environment via `EnvVar`. e.g. 208 | 209 | ``` go 210 | app.Flags = []cli.Flag { 211 | cli.StringFlag{ 212 | Name: "lang, l", 213 | Value: "english", 214 | Usage: "language for the greeting", 215 | EnvVar: "APP_LANG", 216 | }, 217 | } 218 | ``` 219 | 220 | The `EnvVar` may also be given as a comma-delimited "cascade", where the first environment variable that resolves is used as the default. 221 | 222 | ``` go 223 | app.Flags = []cli.Flag { 224 | cli.StringFlag{ 225 | Name: "lang, l", 226 | Value: "english", 227 | Usage: "language for the greeting", 228 | EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG", 229 | }, 230 | } 231 | ``` 232 | 233 | ### Subcommands 234 | 235 | Subcommands can be defined for a more git-like command line app. 236 | ```go 237 | ... 238 | app.Commands = []cli.Command{ 239 | { 240 | Name: "add", 241 | Aliases: []string{"a"}, 242 | Usage: "add a task to the list", 243 | Action: func(c *cli.Context) { 244 | println("added task: ", c.Args().First()) 245 | }, 246 | }, 247 | { 248 | Name: "complete", 249 | Aliases: []string{"c"}, 250 | Usage: "complete a task on the list", 251 | Action: func(c *cli.Context) { 252 | println("completed task: ", c.Args().First()) 253 | }, 254 | }, 255 | { 256 | Name: "template", 257 | Aliases: []string{"r"}, 258 | Usage: "options for task templates", 259 | Subcommands: []cli.Command{ 260 | { 261 | Name: "add", 262 | Usage: "add a new template", 263 | Action: func(c *cli.Context) { 264 | println("new task template: ", c.Args().First()) 265 | }, 266 | }, 267 | { 268 | Name: "remove", 269 | Usage: "remove an existing template", 270 | Action: func(c *cli.Context) { 271 | println("removed task template: ", c.Args().First()) 272 | }, 273 | }, 274 | }, 275 | }, 276 | } 277 | ... 278 | ``` 279 | 280 | ### Bash Completion 281 | 282 | You can enable completion commands by setting the `EnableBashCompletion` 283 | flag on the `App` object. By default, this setting will only auto-complete to 284 | show an app's subcommands, but you can write your own completion methods for 285 | the App or its subcommands. 286 | ```go 287 | ... 288 | var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"} 289 | app := cli.NewApp() 290 | app.EnableBashCompletion = true 291 | app.Commands = []cli.Command{ 292 | { 293 | Name: "complete", 294 | Aliases: []string{"c"}, 295 | Usage: "complete a task on the list", 296 | Action: func(c *cli.Context) { 297 | println("completed task: ", c.Args().First()) 298 | }, 299 | BashComplete: func(c *cli.Context) { 300 | // This will complete if no args are passed 301 | if len(c.Args()) > 0 { 302 | return 303 | } 304 | for _, t := range tasks { 305 | fmt.Println(t) 306 | } 307 | }, 308 | } 309 | } 310 | ... 311 | ``` 312 | 313 | #### To Enable 314 | 315 | Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while 316 | setting the `PROG` variable to the name of your program: 317 | 318 | `PROG=myprogram source /.../cli/autocomplete/bash_autocomplete` 319 | 320 | #### To Distribute 321 | 322 | Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename 323 | it to the name of the program you wish to add autocomplete support for (or 324 | automatically install it there if you are distributing a package). Don't forget 325 | to source the file to make it active in the current shell. 326 | 327 | ``` 328 | sudo cp src/bash_autocomplete /etc/bash_completion.d/ 329 | source /etc/bash_completion.d/ 330 | ``` 331 | 332 | Alternatively, you can just document that users should source the generic 333 | `autocomplete/bash_autocomplete` in their bash configuration with `$PROG` set 334 | to the name of their program (as above). 335 | 336 | ## Contribution Guidelines 337 | Feel free to put up a pull request to fix a bug or maybe add a feature. I will give it a code review and make sure that it does not break backwards compatibility. If I or any other collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch. 338 | 339 | If you have contributed something significant to the project, I will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you have any questions feel free to link @codegangsta to the issue in question and we can review it together. 340 | 341 | If you feel like you have contributed to the project but have not yet been added as a collaborator, I probably forgot to add you. Hit @codegangsta up over email and we will get it figured out. 342 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/JFrogDev/artifactory-cli-go.svg)](https://travis-ci.org/JFrogDev/artifactory-cli-go) 2 | 3 | ## Artifactory CLI 4 | 5 | Artifactory CLI provides a command line interface for uploading and downloading artifacts to and from Artifactory. 6 | It supports Artifactory version 3.5.3 and above. 7 | 8 | ### Deprecated 9 | 10 | ***Artifactory CLI has been deprecated and is no longer being maintained or supported. [JFrog CLI](https://github.com/jfrogdev/jfrog-cli-go) is an evolution of Artifactory CLI, and adds many features including support for Bintray. All features of Artifactory CLI are supported by [JFrog CLI](https://github.com/jfrogdev/jfrog-cli-go) and migrating your scripts is extremely simple. To continue optimizing your file management, please start using [JFrog CLI](https://github.com/jfrogdev/jfrog-cli-go).*** 11 | 12 | ### Getting Started 13 | 14 | #### Downloading the executables from Bintray 15 | 16 | If you do not wish to install/use go, you can [download executables](https://bintray.com/jfrog/artifactory-cli-go) 17 | for Linux, Mac and Windows from JFrog Bintray. Select the architecture you want, download the art executable, and place it in your path. 18 | 19 | #### Building the command line executable 20 | 21 | If you prefer, you may instead build the client in go. 22 | 23 | ##### Setup GO on your machine 24 | 25 | * Make sure you have a working Go environment. [See the install instructions](http://golang.org/doc/install). 26 | * Navigate to the directory where you want to create the *artifactory-cli-go* project. 27 | * Set the value of the GOPATH environment variable to the full path of this directory. 28 | 29 | ##### Download Artifactory CLI from GitHub 30 | 31 | Run the following command to create the *artifactory-cli-go* project: 32 | ```console 33 | $ go get github.com/JFrogDev/artifactory-cli-go/... 34 | ``` 35 | 36 | Navigate to the following directory 37 | ```console 38 | $ cd $GOPATH/bin 39 | ``` 40 | 41 | You'll find the art (or art.exe on Windows) executable there. 42 | 43 | #### Usage 44 | 45 | You can copy the *art* executable to any location on your file-system as long as you add it to your *PATH* environment variable, 46 | so that you can access it from any path. 47 | 48 | ##### Command syntax 49 | 50 | ```console 51 | $ art command-name options arguments 52 | ``` 53 | 54 | The sections below specify the available commands, their respective options and additional arguments that may be needed. 55 | *art* should be followed by a command name (for example, upload), a list of options (for example, --url=http://...) 56 | and the list of arguments for the command. 57 | 58 | ##### The *upload* command 59 | 60 | ###### Function 61 | Used to upload artifacts to Artifactory. 62 | 63 | ###### Command options 64 | ```console 65 | --url [Mandatory] Artifactory URL. 66 | --user [Optional] Artifactory user. 67 | --password [Optional] Artifactory password. 68 | --ssh-key-path [Optional] Path to your private SSH key file. 69 | --props [Optional] List of properties in the form of "key1=value1;key2=value2,..." to be attached to the uploaded artifacts. 70 | --deb [Optional] Used for Debian packages in the form of distribution/component/architecture. 71 | --flat [Default: true] If not set to true, and the upload path ends with a slash, artifacts are uploaded according to their file system hierarchy. 72 | --recursive [Default: true] Set to false if you do not wish to collect artifacts in sub-folders to be uploaded to Artifactory. 73 | --regexp [Default: false] Set to true to use a regular expression instead of wildcards expression to collect artifacts to upload. 74 | --threads [Default: 3] Number of artifacts to upload in parallel. 75 | --dry-run [Default: false] Set to true to disable communication with Artifactory. 76 | ``` 77 | ###### Arguments 78 | * The first argument is the local file-system path to the artifacts to be uploaded to Artifactory. 79 | The path can include a single file or multiple artifacts, by using the * wildcard. 80 | **Important:** If the path is provided as a regular expression (with the --regexp=true option) then 81 | the first regular expression appearing as part of the argument must be enclosed in parenthesis. 82 | 83 | * The second argument is the upload path in Artifactory. 84 | The argument should have the following format: [repository name]/[repository path] 85 | The path can include symbols in the form of {1}, {2}, ... 86 | These symbols are replaced with the sections enclosed with parenthesis in the first argument. 87 | 88 | ###### Examples 89 | 90 | This example uploads the *froggy.tgz* file to the root of the *my-local-repo* repository 91 | ```console 92 | $ art upload "froggy.tgz" "my-local-repo/" --url=http://domain/artifactory --user=admin --password=password 93 | ``` 94 | 95 | This example collects all the zip artifacts located under the build directory (including sub-directories). 96 | and uploads them to the *my-local-repo* repository, under the zipFiles folder, while keeping the artifacts original names. 97 | ```console 98 | $ art upload build/*.zip libs-release-local/zipFiles/ --url=http://domain/artifactory --user=admin --password=password 99 | ``` 100 | And on Windows: 101 | ```console 102 | $ art upload "build\\*.zip" "libs-release-local/zipFiles/" --url=http://domain/artifactory --user=admin --password=password 103 | ``` 104 | 105 | ##### The *download* command 106 | 107 | ###### Function 108 | Used to download artifacts from Artifactory. 109 | 110 | ###### Command options 111 | ```console 112 | --url [Mandatory] Artifactory URL. 113 | --user [Optional] Artifactory user. 114 | --password [Optional] Artifactory password. 115 | --ssh-key-path [Optional] Path to your private SSH key file. 116 | --props [Optional] List of properties in the form of "key1=value1;key2=value2,..." Only artifacts with these properties will be downloaded. 117 | --flat [Default: false] Set to true if you do not wish to have the Artifactory repository path structure created locally for your downloaded artifacts. 118 | --recursive [Default: true] Set to false if you do not wish to include the download of artifacts inside sub-directories in Artifactory. 119 | --min-split [Default: 5120] Minimum file size in KB to split into ranges. Set to -1 for no splits. 120 | --split-count [Default: 3] Number of parts to split a file when downloading. Set to 0 for no splits. 121 | --threads [Default: 3] Number of artifacts to download in parallel. 122 | ``` 123 | 124 | ###### Arguments 125 | The command expects one argument - the path of artifacts to be downloaded from Artifactory. 126 | The argument should have the following format: [repository name]/[repository path] 127 | The path can include a single artifact or multiple artifacts, by using the * wildcard. 128 | The artifacts are downloaded and saved to the current directory, while saving their folder structure. 129 | 130 | ###### Examples 131 | 132 | This example downloads the *cool-froggy.zip* artifact located at the root of the *my-local-repo* repository to current directory. 133 | ```console 134 | $ art download "my-local-repo/cool-froggy.zip" --url=http://domain/artifactory --user=admin --password=password 135 | ``` 136 | 137 | This example downloads all artifacts located in the *my-local-repo* repository under the *all-my-frogs* folder to the *all-my-frog* directory located unde the current directory. 138 | ```console 139 | $ art download "my-local-repo/all-my-frogs/" --url=http://domain/artifactory --user=admin --password=password 140 | ``` 141 | 142 | ##### The *config* command 143 | 144 | ###### Function 145 | Used to configure the Artifactory URL, user and passwords, so that you don't have to send them as options 146 | for the *upload* and *download* commands. 147 | The configuration is saved at ~/.jfrog/art-cli.conf 148 | 149 | ###### Command options 150 | ```console 151 | --interactive [Default: true] Set to false if you do not wish the config command to be interactive. If true, the --url option becomes optional. 152 | --enc-password [Default: true] If set to false then the configured password will not be encrypted using Artifatory's encryption API. 153 | --url [Optional] Artifactory URL. 154 | --user [Optional] Artifactory user. 155 | --password [Optional] Artifactory password. 156 | --ssh-key-path [Optional] Path to your private SSH key file. 157 | ``` 158 | 159 | ###### Arguments 160 | * If no arguments are sent, the command will configure the Artifactory URL, user and password sent through the command options 161 | or through the command's interactive prompt. 162 | * The *show* argument will make the command show the stored configuration. 163 | * The *clear* argument will make the command clear the stored configuration. 164 | 165 | ###### Important Note 166 | 167 | if your Artifactory server has [encrypted password set to required](https://www.jfrog.com/confluence/display/RTF/Configuring+Security#ConfiguringSecurity-PasswordEncryptionPolicy) you should use your API Key as your password. 168 | 169 | ###### Examples 170 | 171 | Configure the Artifactory details through an interactive propmp. 172 | ```console 173 | $ art config 174 | ``` 175 | 176 | Configure the Artifactory details through the command options. 177 | 178 | ```console 179 | $ art config --url=http://domain/artifactory --user=admin --password=password 180 | ``` 181 | 182 | Show the configured Artifactory details. 183 | ```console 184 | $ art config show 185 | ``` 186 | 187 | Clear the configured Artifactory details. 188 | ```console 189 | $ art config clear 190 | ``` 191 | 192 | #### Security 193 | Artifactory CLI lets you authenticate yourself to Artifactory either using your Artifactory user and password, or using RSA public and private keys. 194 | 195 | ##### Authentication with user and password 196 | To use your Artifactory user and password, simply use the *--user* and *--password* command options as described above or configure your user and password using the *config* command. 197 | 198 | ##### Authentication using RSA public and private keys 199 | Artifactory supports SSH authentication using RSA public and private keys. SSH authentication is supported from version 4.4 with version 1.3.0 and bove of the Artifactory CLI. 200 | To authenticate yourself to Artifactory using RSA keys, execute the following instructions: 201 | * Enable SSH authentication in Artifactory as described in the [Artifactory Documentation](https://www.jfrog.com/confluence/display/RTF/SSH+Integration). 202 | * Configure your Artifactory URL to have the following format: *ssh://[host]:[port]* using the *--url* command option or the config command. 203 | Please make sure the *[host]* URL section does not include your Artifactory context URL, but only the host name or IP. 204 | * Configure the path to your private SSH key file using the *--ssh-key-path* command option or the *config* command. 205 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/crypto/ssh/common.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ssh 6 | 7 | import ( 8 | "crypto" 9 | "crypto/rand" 10 | "fmt" 11 | "io" 12 | "sync" 13 | 14 | _ "crypto/sha1" 15 | _ "crypto/sha256" 16 | _ "crypto/sha512" 17 | ) 18 | 19 | // These are string constants in the SSH protocol. 20 | const ( 21 | compressionNone = "none" 22 | serviceUserAuth = "ssh-userauth" 23 | serviceSSH = "ssh-connection" 24 | ) 25 | 26 | // supportedCiphers specifies the supported ciphers in preference order. 27 | var supportedCiphers = []string{ 28 | "aes128-ctr", "aes192-ctr", "aes256-ctr", 29 | "aes128-gcm@openssh.com", 30 | "arcfour256", "arcfour128", 31 | } 32 | 33 | // supportedKexAlgos specifies the supported key-exchange algorithms in 34 | // preference order. 35 | var supportedKexAlgos = []string{ 36 | kexAlgoCurve25519SHA256, 37 | // P384 and P521 are not constant-time yet, but since we don't 38 | // reuse ephemeral keys, using them for ECDH should be OK. 39 | kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, 40 | kexAlgoDH14SHA1, kexAlgoDH1SHA1, 41 | } 42 | 43 | // supportedKexAlgos specifies the supported host-key algorithms (i.e. methods 44 | // of authenticating servers) in preference order. 45 | var supportedHostKeyAlgos = []string{ 46 | CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, 47 | CertAlgoECDSA384v01, CertAlgoECDSA521v01, 48 | 49 | KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, 50 | KeyAlgoRSA, KeyAlgoDSA, 51 | } 52 | 53 | // supportedMACs specifies a default set of MAC algorithms in preference order. 54 | // This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed 55 | // because they have reached the end of their useful life. 56 | var supportedMACs = []string{ 57 | "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96", 58 | } 59 | 60 | var supportedCompressions = []string{compressionNone} 61 | 62 | // hashFuncs keeps the mapping of supported algorithms to their respective 63 | // hashes needed for signature verification. 64 | var hashFuncs = map[string]crypto.Hash{ 65 | KeyAlgoRSA: crypto.SHA1, 66 | KeyAlgoDSA: crypto.SHA1, 67 | KeyAlgoECDSA256: crypto.SHA256, 68 | KeyAlgoECDSA384: crypto.SHA384, 69 | KeyAlgoECDSA521: crypto.SHA512, 70 | CertAlgoRSAv01: crypto.SHA1, 71 | CertAlgoDSAv01: crypto.SHA1, 72 | CertAlgoECDSA256v01: crypto.SHA256, 73 | CertAlgoECDSA384v01: crypto.SHA384, 74 | CertAlgoECDSA521v01: crypto.SHA512, 75 | } 76 | 77 | // unexpectedMessageError results when the SSH message that we received didn't 78 | // match what we wanted. 79 | func unexpectedMessageError(expected, got uint8) error { 80 | return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected) 81 | } 82 | 83 | // parseError results from a malformed SSH message. 84 | func parseError(tag uint8) error { 85 | return fmt.Errorf("ssh: parse error in message type %d", tag) 86 | } 87 | 88 | func findCommon(what string, client []string, server []string) (common string, err error) { 89 | for _, c := range client { 90 | for _, s := range server { 91 | if c == s { 92 | return c, nil 93 | } 94 | } 95 | } 96 | return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server) 97 | } 98 | 99 | type directionAlgorithms struct { 100 | Cipher string 101 | MAC string 102 | Compression string 103 | } 104 | 105 | type algorithms struct { 106 | kex string 107 | hostKey string 108 | w directionAlgorithms 109 | r directionAlgorithms 110 | } 111 | 112 | func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) { 113 | result := &algorithms{} 114 | 115 | result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos) 116 | if err != nil { 117 | return 118 | } 119 | 120 | result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos) 121 | if err != nil { 122 | return 123 | } 124 | 125 | result.w.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) 126 | if err != nil { 127 | return 128 | } 129 | 130 | result.r.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) 131 | if err != nil { 132 | return 133 | } 134 | 135 | result.w.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) 136 | if err != nil { 137 | return 138 | } 139 | 140 | result.r.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) 141 | if err != nil { 142 | return 143 | } 144 | 145 | result.w.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) 146 | if err != nil { 147 | return 148 | } 149 | 150 | result.r.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) 151 | if err != nil { 152 | return 153 | } 154 | 155 | return result, nil 156 | } 157 | 158 | // If rekeythreshold is too small, we can't make any progress sending 159 | // stuff. 160 | const minRekeyThreshold uint64 = 256 161 | 162 | // Config contains configuration data common to both ServerConfig and 163 | // ClientConfig. 164 | type Config struct { 165 | // Rand provides the source of entropy for cryptographic 166 | // primitives. If Rand is nil, the cryptographic random reader 167 | // in package crypto/rand will be used. 168 | Rand io.Reader 169 | 170 | // The maximum number of bytes sent or received after which a 171 | // new key is negotiated. It must be at least 256. If 172 | // unspecified, 1 gigabyte is used. 173 | RekeyThreshold uint64 174 | 175 | // The allowed key exchanges algorithms. If unspecified then a 176 | // default set of algorithms is used. 177 | KeyExchanges []string 178 | 179 | // The allowed cipher algorithms. If unspecified then a sensible 180 | // default is used. 181 | Ciphers []string 182 | 183 | // The allowed MAC algorithms. If unspecified then a sensible default 184 | // is used. 185 | MACs []string 186 | } 187 | 188 | // SetDefaults sets sensible values for unset fields in config. This is 189 | // exported for testing: Configs passed to SSH functions are copied and have 190 | // default values set automatically. 191 | func (c *Config) SetDefaults() { 192 | if c.Rand == nil { 193 | c.Rand = rand.Reader 194 | } 195 | if c.Ciphers == nil { 196 | c.Ciphers = supportedCiphers 197 | } 198 | var ciphers []string 199 | for _, c := range c.Ciphers { 200 | if cipherModes[c] != nil { 201 | // reject the cipher if we have no cipherModes definition 202 | ciphers = append(ciphers, c) 203 | } 204 | } 205 | c.Ciphers = ciphers 206 | 207 | if c.KeyExchanges == nil { 208 | c.KeyExchanges = supportedKexAlgos 209 | } 210 | 211 | if c.MACs == nil { 212 | c.MACs = supportedMACs 213 | } 214 | 215 | if c.RekeyThreshold == 0 { 216 | // RFC 4253, section 9 suggests rekeying after 1G. 217 | c.RekeyThreshold = 1 << 30 218 | } 219 | if c.RekeyThreshold < minRekeyThreshold { 220 | c.RekeyThreshold = minRekeyThreshold 221 | } 222 | } 223 | 224 | // buildDataSignedForAuth returns the data that is signed in order to prove 225 | // possession of a private key. See RFC 4252, section 7. 226 | func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { 227 | data := struct { 228 | Session []byte 229 | Type byte 230 | User string 231 | Service string 232 | Method string 233 | Sign bool 234 | Algo []byte 235 | PubKey []byte 236 | }{ 237 | sessionId, 238 | msgUserAuthRequest, 239 | req.User, 240 | req.Service, 241 | req.Method, 242 | true, 243 | algo, 244 | pubKey, 245 | } 246 | return Marshal(data) 247 | } 248 | 249 | func appendU16(buf []byte, n uint16) []byte { 250 | return append(buf, byte(n>>8), byte(n)) 251 | } 252 | 253 | func appendU32(buf []byte, n uint32) []byte { 254 | return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) 255 | } 256 | 257 | func appendU64(buf []byte, n uint64) []byte { 258 | return append(buf, 259 | byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), 260 | byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) 261 | } 262 | 263 | func appendInt(buf []byte, n int) []byte { 264 | return appendU32(buf, uint32(n)) 265 | } 266 | 267 | func appendString(buf []byte, s string) []byte { 268 | buf = appendU32(buf, uint32(len(s))) 269 | buf = append(buf, s...) 270 | return buf 271 | } 272 | 273 | func appendBool(buf []byte, b bool) []byte { 274 | if b { 275 | return append(buf, 1) 276 | } 277 | return append(buf, 0) 278 | } 279 | 280 | // newCond is a helper to hide the fact that there is no usable zero 281 | // value for sync.Cond. 282 | func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) } 283 | 284 | // window represents the buffer available to clients 285 | // wishing to write to a channel. 286 | type window struct { 287 | *sync.Cond 288 | win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1 289 | writeWaiters int 290 | closed bool 291 | } 292 | 293 | // add adds win to the amount of window available 294 | // for consumers. 295 | func (w *window) add(win uint32) bool { 296 | // a zero sized window adjust is a noop. 297 | if win == 0 { 298 | return true 299 | } 300 | w.L.Lock() 301 | if w.win+win < win { 302 | w.L.Unlock() 303 | return false 304 | } 305 | w.win += win 306 | // It is unusual that multiple goroutines would be attempting to reserve 307 | // window space, but not guaranteed. Use broadcast to notify all waiters 308 | // that additional window is available. 309 | w.Broadcast() 310 | w.L.Unlock() 311 | return true 312 | } 313 | 314 | // close sets the window to closed, so all reservations fail 315 | // immediately. 316 | func (w *window) close() { 317 | w.L.Lock() 318 | w.closed = true 319 | w.Broadcast() 320 | w.L.Unlock() 321 | } 322 | 323 | // reserve reserves win from the available window capacity. 324 | // If no capacity remains, reserve will block. reserve may 325 | // return less than requested. 326 | func (w *window) reserve(win uint32) (uint32, error) { 327 | var err error 328 | w.L.Lock() 329 | w.writeWaiters++ 330 | w.Broadcast() 331 | for w.win == 0 && !w.closed { 332 | w.Wait() 333 | } 334 | w.writeWaiters-- 335 | if w.win < win { 336 | win = w.win 337 | } 338 | w.win -= win 339 | if w.closed { 340 | err = io.EOF 341 | } 342 | w.L.Unlock() 343 | return win, err 344 | } 345 | 346 | // waitWriterBlocked waits until some goroutine is blocked for further 347 | // writes. It is used in tests only. 348 | func (w *window) waitWriterBlocked() { 349 | w.Cond.L.Lock() 350 | for w.writeWaiters == 0 { 351 | w.Cond.Wait() 352 | } 353 | w.Cond.L.Unlock() 354 | } 355 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/context.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "strconv" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | // Context is a type that is passed through to 12 | // each Handler action in a cli application. Context 13 | // can be used to retrieve context-specific Args and 14 | // parsed command-line options. 15 | type Context struct { 16 | App *App 17 | Command Command 18 | flagSet *flag.FlagSet 19 | setFlags map[string]bool 20 | globalSetFlags map[string]bool 21 | parentContext *Context 22 | } 23 | 24 | // Creates a new context. For use in when invoking an App or Command action. 25 | func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { 26 | return &Context{App: app, flagSet: set, parentContext: parentCtx} 27 | } 28 | 29 | // Looks up the value of a local int flag, returns 0 if no int flag exists 30 | func (c *Context) Int(name string) int { 31 | return lookupInt(name, c.flagSet) 32 | } 33 | 34 | // Looks up the value of a local time.Duration flag, returns 0 if no time.Duration flag exists 35 | func (c *Context) Duration(name string) time.Duration { 36 | return lookupDuration(name, c.flagSet) 37 | } 38 | 39 | // Looks up the value of a local float64 flag, returns 0 if no float64 flag exists 40 | func (c *Context) Float64(name string) float64 { 41 | return lookupFloat64(name, c.flagSet) 42 | } 43 | 44 | // Looks up the value of a local bool flag, returns false if no bool flag exists 45 | func (c *Context) Bool(name string) bool { 46 | return lookupBool(name, c.flagSet) 47 | } 48 | 49 | // Looks up the value of a local boolT flag, returns false if no bool flag exists 50 | func (c *Context) BoolT(name string) bool { 51 | return lookupBoolT(name, c.flagSet) 52 | } 53 | 54 | // Looks up the value of a local string flag, returns "" if no string flag exists 55 | func (c *Context) String(name string) string { 56 | return lookupString(name, c.flagSet) 57 | } 58 | 59 | // Looks up the value of a local string slice flag, returns nil if no string slice flag exists 60 | func (c *Context) StringSlice(name string) []string { 61 | return lookupStringSlice(name, c.flagSet) 62 | } 63 | 64 | // Looks up the value of a local int slice flag, returns nil if no int slice flag exists 65 | func (c *Context) IntSlice(name string) []int { 66 | return lookupIntSlice(name, c.flagSet) 67 | } 68 | 69 | // Looks up the value of a local generic flag, returns nil if no generic flag exists 70 | func (c *Context) Generic(name string) interface{} { 71 | return lookupGeneric(name, c.flagSet) 72 | } 73 | 74 | // Looks up the value of a global int flag, returns 0 if no int flag exists 75 | func (c *Context) GlobalInt(name string) int { 76 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 77 | return lookupInt(name, fs) 78 | } 79 | return 0 80 | } 81 | 82 | // Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists 83 | func (c *Context) GlobalDuration(name string) time.Duration { 84 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 85 | return lookupDuration(name, fs) 86 | } 87 | return 0 88 | } 89 | 90 | // Looks up the value of a global bool flag, returns false if no bool flag exists 91 | func (c *Context) GlobalBool(name string) bool { 92 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 93 | return lookupBool(name, fs) 94 | } 95 | return false 96 | } 97 | 98 | // Looks up the value of a global string flag, returns "" if no string flag exists 99 | func (c *Context) GlobalString(name string) string { 100 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 101 | return lookupString(name, fs) 102 | } 103 | return "" 104 | } 105 | 106 | // Looks up the value of a global string slice flag, returns nil if no string slice flag exists 107 | func (c *Context) GlobalStringSlice(name string) []string { 108 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 109 | return lookupStringSlice(name, fs) 110 | } 111 | return nil 112 | } 113 | 114 | // Looks up the value of a global int slice flag, returns nil if no int slice flag exists 115 | func (c *Context) GlobalIntSlice(name string) []int { 116 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 117 | return lookupIntSlice(name, fs) 118 | } 119 | return nil 120 | } 121 | 122 | // Looks up the value of a global generic flag, returns nil if no generic flag exists 123 | func (c *Context) GlobalGeneric(name string) interface{} { 124 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 125 | return lookupGeneric(name, fs) 126 | } 127 | return nil 128 | } 129 | 130 | // Returns the number of flags set 131 | func (c *Context) NumFlags() int { 132 | return c.flagSet.NFlag() 133 | } 134 | 135 | // Determines if the flag was actually set 136 | func (c *Context) IsSet(name string) bool { 137 | if c.setFlags == nil { 138 | c.setFlags = make(map[string]bool) 139 | c.flagSet.Visit(func(f *flag.Flag) { 140 | c.setFlags[f.Name] = true 141 | }) 142 | } 143 | return c.setFlags[name] == true 144 | } 145 | 146 | // Determines if the global flag was actually set 147 | func (c *Context) GlobalIsSet(name string) bool { 148 | if c.globalSetFlags == nil { 149 | c.globalSetFlags = make(map[string]bool) 150 | ctx := c 151 | if ctx.parentContext != nil { 152 | ctx = ctx.parentContext 153 | } 154 | for ; ctx != nil && c.globalSetFlags[name] == false; ctx = ctx.parentContext { 155 | ctx.flagSet.Visit(func(f *flag.Flag) { 156 | c.globalSetFlags[f.Name] = true 157 | }) 158 | } 159 | } 160 | return c.globalSetFlags[name] 161 | } 162 | 163 | // Returns a slice of flag names used in this context. 164 | func (c *Context) FlagNames() (names []string) { 165 | for _, flag := range c.Command.Flags { 166 | name := strings.Split(flag.GetName(), ",")[0] 167 | if name == "help" { 168 | continue 169 | } 170 | names = append(names, name) 171 | } 172 | return 173 | } 174 | 175 | // Returns a slice of global flag names used by the app. 176 | func (c *Context) GlobalFlagNames() (names []string) { 177 | for _, flag := range c.App.Flags { 178 | name := strings.Split(flag.GetName(), ",")[0] 179 | if name == "help" || name == "version" { 180 | continue 181 | } 182 | names = append(names, name) 183 | } 184 | return 185 | } 186 | 187 | // Returns the parent context, if any 188 | func (c *Context) Parent() *Context { 189 | return c.parentContext 190 | } 191 | 192 | type Args []string 193 | 194 | // Returns the command line arguments associated with the context. 195 | func (c *Context) Args() Args { 196 | args := Args(c.flagSet.Args()) 197 | return args 198 | } 199 | 200 | // Returns the nth argument, or else a blank string 201 | func (a Args) Get(n int) string { 202 | if len(a) > n { 203 | return a[n] 204 | } 205 | return "" 206 | } 207 | 208 | // Returns the first argument, or else a blank string 209 | func (a Args) First() string { 210 | return a.Get(0) 211 | } 212 | 213 | // Return the rest of the arguments (not the first one) 214 | // or else an empty string slice 215 | func (a Args) Tail() []string { 216 | if len(a) >= 2 { 217 | return []string(a)[1:] 218 | } 219 | return []string{} 220 | } 221 | 222 | // Checks if there are any arguments present 223 | func (a Args) Present() bool { 224 | return len(a) != 0 225 | } 226 | 227 | // Swaps arguments at the given indexes 228 | func (a Args) Swap(from, to int) error { 229 | if from >= len(a) || to >= len(a) { 230 | return errors.New("index out of range") 231 | } 232 | a[from], a[to] = a[to], a[from] 233 | return nil 234 | } 235 | 236 | func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { 237 | if ctx.parentContext != nil { 238 | ctx = ctx.parentContext 239 | } 240 | for ; ctx != nil; ctx = ctx.parentContext { 241 | if f := ctx.flagSet.Lookup(name); f != nil { 242 | return ctx.flagSet 243 | } 244 | } 245 | return nil 246 | } 247 | 248 | func lookupInt(name string, set *flag.FlagSet) int { 249 | f := set.Lookup(name) 250 | if f != nil { 251 | val, err := strconv.Atoi(f.Value.String()) 252 | if err != nil { 253 | return 0 254 | } 255 | return val 256 | } 257 | 258 | return 0 259 | } 260 | 261 | func lookupDuration(name string, set *flag.FlagSet) time.Duration { 262 | f := set.Lookup(name) 263 | if f != nil { 264 | val, err := time.ParseDuration(f.Value.String()) 265 | if err == nil { 266 | return val 267 | } 268 | } 269 | 270 | return 0 271 | } 272 | 273 | func lookupFloat64(name string, set *flag.FlagSet) float64 { 274 | f := set.Lookup(name) 275 | if f != nil { 276 | val, err := strconv.ParseFloat(f.Value.String(), 64) 277 | if err != nil { 278 | return 0 279 | } 280 | return val 281 | } 282 | 283 | return 0 284 | } 285 | 286 | func lookupString(name string, set *flag.FlagSet) string { 287 | f := set.Lookup(name) 288 | if f != nil { 289 | return f.Value.String() 290 | } 291 | 292 | return "" 293 | } 294 | 295 | func lookupStringSlice(name string, set *flag.FlagSet) []string { 296 | f := set.Lookup(name) 297 | if f != nil { 298 | return (f.Value.(*StringSlice)).Value() 299 | 300 | } 301 | 302 | return nil 303 | } 304 | 305 | func lookupIntSlice(name string, set *flag.FlagSet) []int { 306 | f := set.Lookup(name) 307 | if f != nil { 308 | return (f.Value.(*IntSlice)).Value() 309 | 310 | } 311 | 312 | return nil 313 | } 314 | 315 | func lookupGeneric(name string, set *flag.FlagSet) interface{} { 316 | f := set.Lookup(name) 317 | if f != nil { 318 | return f.Value 319 | } 320 | return nil 321 | } 322 | 323 | func lookupBool(name string, set *flag.FlagSet) bool { 324 | f := set.Lookup(name) 325 | if f != nil { 326 | val, err := strconv.ParseBool(f.Value.String()) 327 | if err != nil { 328 | return false 329 | } 330 | return val 331 | } 332 | 333 | return false 334 | } 335 | 336 | func lookupBoolT(name string, set *flag.FlagSet) bool { 337 | f := set.Lookup(name) 338 | if f != nil { 339 | val, err := strconv.ParseBool(f.Value.String()) 340 | if err != nil { 341 | return true 342 | } 343 | return val 344 | } 345 | 346 | return false 347 | } 348 | 349 | func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { 350 | switch ff.Value.(type) { 351 | case *StringSlice: 352 | default: 353 | set.Set(name, ff.Value.String()) 354 | } 355 | } 356 | 357 | func normalizeFlags(flags []Flag, set *flag.FlagSet) error { 358 | visited := make(map[string]bool) 359 | set.Visit(func(f *flag.Flag) { 360 | visited[f.Name] = true 361 | }) 362 | for _, f := range flags { 363 | parts := strings.Split(f.GetName(), ",") 364 | if len(parts) == 1 { 365 | continue 366 | } 367 | var ff *flag.Flag 368 | for _, name := range parts { 369 | name = strings.Trim(name, " ") 370 | if visited[name] { 371 | if ff != nil { 372 | return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) 373 | } 374 | ff = set.Lookup(name) 375 | } 376 | } 377 | if ff == nil { 378 | continue 379 | } 380 | for _, name := range parts { 381 | name = strings.Trim(name, " ") 382 | if !visited[name] { 383 | copyFlag(name, ff, set) 384 | } 385 | } 386 | } 387 | return nil 388 | } 389 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /art/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "strconv" 7 | "github.com/JFrogDev/artifactory-cli-go/utils" 8 | "github.com/JFrogDev/artifactory-cli-go/commands" 9 | "github.com/JFrogDev/artifactory-cli-go/Godeps/_workspace/src/github.com/codegangsta/cli" 10 | ) 11 | 12 | var flags = new(utils.Flags) 13 | 14 | func main() { 15 | utils.CreateTempDirPath() 16 | defer utils.RemoveTempDir() 17 | 18 | app := cli.NewApp() 19 | app.Name = "art" 20 | app.Usage = "See https://github.com/JFrogDev/artifactory-cli-go for usage instructions." 21 | app.Version = utils.GetVersion() 22 | 23 | app.Commands = []cli.Command{ 24 | { 25 | Name: "config", 26 | Flags: getConfigFlags(), 27 | Aliases: []string{"c"}, 28 | Usage: "config", 29 | Action: func(c *cli.Context) { 30 | config(c) 31 | }, 32 | }, 33 | { 34 | Name: "upload", 35 | Flags: getUploadFlags(), 36 | Aliases: []string{"u"}, 37 | Usage: "upload ", 38 | Action: func(c *cli.Context) { 39 | upload(c) 40 | }, 41 | }, 42 | { 43 | Name: "download", 44 | Flags: getDownloadFlags(), 45 | Aliases: []string{"d"}, 46 | Usage: "download ", 47 | Action: func(c *cli.Context) { 48 | download(c) 49 | }, 50 | }, 51 | } 52 | 53 | app.Run(os.Args) 54 | } 55 | 56 | func getFlags() []cli.Flag { 57 | return []cli.Flag{ 58 | cli.StringFlag{ 59 | Name: "url", 60 | Usage: "[Mandatory] Artifactory URL", 61 | }, 62 | cli.StringFlag{ 63 | Name: "user", 64 | Usage: "[Optional] Artifactory username", 65 | }, 66 | cli.StringFlag{ 67 | Name: "password", 68 | Usage: "[Optional] Artifactory password", 69 | }, 70 | cli.StringFlag{ 71 | Name: "ssh-key-path", 72 | Usage: "[Optional] SSH key file path", 73 | }, 74 | } 75 | } 76 | 77 | func getUploadFlags() []cli.Flag { 78 | flags := []cli.Flag{ 79 | nil, nil,nil,nil,nil,nil,nil,nil,nil,nil,nil, 80 | } 81 | copy(flags[0:4], getFlags()) 82 | flags[4] = cli.StringFlag{ 83 | Name: "props", 84 | Usage: "[Optional] List of properties in the form of \"key1=value1;key2=value2,...\" to be attached to the uploaded artifacts.", 85 | } 86 | flags[5] = cli.StringFlag{ 87 | Name: "deb", 88 | Usage: "[Optional] Used for Debian packages in the form of distribution/component/architecture.", 89 | } 90 | flags[6] = cli.StringFlag{ 91 | Name: "recursive", 92 | Value: "", 93 | Usage: "[Default: true] Set to false if you do not wish to collect artifacts in sub-folders to be uploaded to Artifactory.", 94 | } 95 | flags[7] = cli.StringFlag{ 96 | Name: "flat", 97 | Value: "", 98 | Usage: "[Default: true] If not set to true, and the upload path ends with a slash, files are uploaded according to their file system hierarchy.", 99 | } 100 | flags[8] = cli.BoolFlag{ 101 | Name: "regexp", 102 | Usage: "[Default: false] Set to true to use a regular expression instead of wildcards expression to collect files to upload.", 103 | } 104 | flags[9] = cli.StringFlag{ 105 | Name: "threads", 106 | Value: "", 107 | Usage: "[Default: 3] Number of artifacts to upload in parallel.", 108 | } 109 | flags[10] = cli.BoolFlag{ 110 | Name: "dry-run", 111 | Usage: "[Default: false] Set to true to disable communication with Artifactory.", 112 | } 113 | return flags 114 | } 115 | 116 | func getDownloadFlags() []cli.Flag { 117 | flags := []cli.Flag{ 118 | nil,nil,nil,nil,nil,nil,nil,nil,nil,nil, 119 | } 120 | copy(flags[0:4], getFlags()) 121 | flags[4] = cli.StringFlag{ 122 | Name: "props", 123 | Usage: "[Optional] List of properties in the form of \"key1=value1;key2=value2,...\" Only artifacts with these properties will be downloaded.", 124 | } 125 | flags[5] = cli.StringFlag{ 126 | Name: "recursive", 127 | Value: "", 128 | Usage: "[Default: true] Set to false if you do not wish to include the download of artifacts inside sub-folders in Artifactory.", 129 | } 130 | flags[6] = cli.StringFlag{ 131 | Name: "flat", 132 | Value: "", 133 | Usage: "[Default: false] Set to true if you do not wish to have the Artifactory repository path structure created locally for your downloaded files.", 134 | } 135 | flags[7] = cli.StringFlag{ 136 | Name: "min-split", 137 | Value: "", 138 | Usage: "[Default: 5120] Minimum file size in KB to split into ranges when downloading. Set to -1 for no splits.", 139 | } 140 | flags[8] = cli.StringFlag{ 141 | Name: "split-count", 142 | Value: "", 143 | Usage: "[Default: 3] Number of parts to split a file when downloading. Set to 0 for no splits.", 144 | } 145 | flags[9] = cli.StringFlag{ 146 | Name: "threads", 147 | Value: "", 148 | Usage: "[Default: 3] Number of artifacts to download in parallel.", 149 | } 150 | return flags 151 | } 152 | 153 | func getConfigFlags() []cli.Flag { 154 | flags := []cli.Flag{ 155 | nil,nil,nil,nil,nil,nil, 156 | } 157 | flags[0] = cli.StringFlag{ 158 | Name: "interactive", 159 | Usage: "[Default: true] Set to false if you do not want the config command to be interactive. If true, the --url option becomes optional.", 160 | } 161 | flags[1] = cli.StringFlag{ 162 | Name: "enc-password", 163 | Usage: "[Default: true] If set to false then the configured password will not be encrypted using Artifatory's encryption API.", 164 | } 165 | copy(flags[2:6], getFlags()) 166 | return flags 167 | } 168 | 169 | func initFlags(c *cli.Context, cmd string) { 170 | if c.String("recursive") == "" { 171 | flags.Recursive = true 172 | } else { 173 | flags.Recursive = c.Bool("recursive") 174 | } 175 | if c.String("interactive") == "" { 176 | flags.Interactive = true 177 | } else { 178 | flags.Interactive = c.Bool("interactive") 179 | } 180 | if c.String("enc-password") == "" { 181 | flags.EncPassword = true 182 | } else { 183 | flags.EncPassword = c.Bool("enc-password") 184 | } 185 | 186 | if cmd == "config" { 187 | flags.ArtDetails = getArtifactoryDetails(c, false) 188 | if !flags.Interactive && flags.ArtDetails.Url == "" { 189 | utils.Exit(utils.ExitCodeError, "The --url option is mandatory when the --interactive option is set to false") 190 | } 191 | } else { 192 | flags.ArtDetails = getArtifactoryDetails(c, true) 193 | if flags.ArtDetails.Url == "" { 194 | utils.Exit(utils.ExitCodeError, "The --url option is mandatory") 195 | } 196 | } 197 | 198 | strFlat := c.String("flat") 199 | if cmd == "upload" { 200 | if strFlat == "" { 201 | flags.Flat = true 202 | } else { 203 | flags.Flat, _ = strconv.ParseBool(strFlat) 204 | } 205 | } else { 206 | if strFlat == "" { 207 | flags.Flat = false 208 | } else { 209 | flags.Flat, _ = strconv.ParseBool(strFlat) 210 | } 211 | } 212 | 213 | flags.Deb = c.String("deb") 214 | if flags.Deb != "" && len(strings.Split(flags.Deb, "/")) != 3 { 215 | utils.Exit(utils.ExitCodeError, "The --deb option should be in the form of distribution/component/architecture") 216 | } 217 | flags.Props = c.String("props") 218 | flags.DryRun = c.Bool("dry-run") 219 | flags.UseRegExp = c.Bool("regexp") 220 | var err error 221 | if c.String("threads") == "" { 222 | flags.Threads = 3 223 | } else { 224 | flags.Threads, err = strconv.Atoi(c.String("threads")) 225 | if err != nil || flags.Threads < 1 { 226 | utils.Exit(utils.ExitCodeError, "The '--threads' option should have a numeric positive value.") 227 | } 228 | } 229 | if c.String("min-split") == "" { 230 | flags.MinSplitSize = 5120 231 | } else { 232 | flags.MinSplitSize, err = strconv.ParseInt(c.String("min-split"), 10, 64) 233 | if err != nil { 234 | utils.Exit(utils.ExitCodeError, "The '--min-split' option should have a numeric value. Try 'art download --help'.") 235 | } 236 | } 237 | if c.String("split-count") == "" { 238 | flags.SplitCount = 3 239 | } else { 240 | flags.SplitCount, err = strconv.Atoi(c.String("split-count")) 241 | if err != nil { 242 | utils.Exit(utils.ExitCodeError, "The '--split-count' option should have a numeric value. Try 'art download --help'.") 243 | } 244 | if flags.SplitCount > 15 { 245 | utils.Exit(utils.ExitCodeError, "The '--split-count' option value is limitted to a maximum of 15.") 246 | } 247 | if flags.SplitCount < 0 { 248 | utils.Exit(utils.ExitCodeError, "The '--split-count' option cannot have a negative value.") 249 | } 250 | } 251 | } 252 | 253 | func config(c *cli.Context) { 254 | if len(c.Args()) > 1 { 255 | utils.Exit(utils.ExitCodeError, "Wrong number of arguments. Try 'art config --help'.") 256 | } else 257 | if len(c.Args()) == 1 { 258 | if c.Args()[0] == "show" { 259 | commands.ShowConfig() 260 | } else 261 | if c.Args()[0] == "clear" { 262 | commands.ClearConfig() 263 | } else { 264 | utils.Exit(utils.ExitCodeError, "Unknown argument '" + c.Args()[0] + "'. Available arguments are 'show' and 'clear'.") 265 | } 266 | } else { 267 | initFlags(c, "config") 268 | commands.Config(flags.ArtDetails, flags.Interactive, flags.EncPassword) 269 | } 270 | } 271 | 272 | func download(c *cli.Context) { 273 | initFlags(c, "download") 274 | if len(c.Args()) != 1 { 275 | utils.Exit(utils.ExitCodeError, "Wrong number of arguments. Try 'art download --help'.") 276 | } 277 | pattern := c.Args()[0] 278 | commands.Download(pattern, flags) 279 | } 280 | 281 | func upload(c *cli.Context) { 282 | initFlags(c, "upload") 283 | size := len(c.Args()) 284 | if size != 2 { 285 | utils.Exit(utils.ExitCodeError, "Wrong number of arguments. Try 'art upload --help'.") 286 | } 287 | localPath := c.Args()[0] 288 | targetPath := c.Args()[1] 289 | uploaded , failed := commands.Upload(localPath, targetPath, flags) 290 | if failed > 0 { 291 | if uploaded > 0 { 292 | utils.Exit(utils.ExitCodeWarning, "") 293 | } 294 | utils.Exit(utils.ExitCodeError, "") 295 | } 296 | } 297 | 298 | func getArtifactoryDetails(c *cli.Context, includeConfig bool) *utils.ArtifactoryDetails { 299 | details := new(utils.ArtifactoryDetails) 300 | details.Url = c.String("url") 301 | details.User = c.String("user") 302 | details.Password = c.String("password") 303 | details.SshKeyPath = c.String("ssh-key-path") 304 | 305 | if includeConfig { 306 | if details.Url == "" || 307 | ((details.User == "" || details.Password == "") && details.SshKeyPath == "") { 308 | 309 | confDetails := commands.GetConfig() 310 | if details.Url == "" { 311 | details.Url = confDetails.Url 312 | } 313 | if details.User == "" { 314 | details.User = confDetails.User 315 | } 316 | if details.Password == "" { 317 | details.Password = confDetails.Password 318 | } 319 | if details.SshKeyPath == "" { 320 | details.SshKeyPath = confDetails.SshKeyPath 321 | } 322 | } 323 | } 324 | details.Url = utils.AddTrailingSlashIfNeeded(details.Url) 325 | return details 326 | } --------------------------------------------------------------------------------