2011-01-28 07:07:18 +00:00
/ *
2013-12-10 16:52:52 +00:00
Copyright 2011 The Camlistore Authors .
2011-01-28 07:07:18 +00:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
2011-01-02 22:36:03 +00:00
package client
2010-12-20 22:54:41 +00:00
import (
2014-01-05 02:43:58 +00:00
"errors"
2010-12-20 22:54:41 +00:00
"flag"
2013-06-21 14:10:40 +00:00
"fmt"
Update from r60 to [almost] Go 1.
A lot is still broken, but most stuff at least compiles now.
The directory tree has been rearranged now too. Go libraries are now
under "pkg". Fully qualified, they are e.g. "camlistore.org/pkg/jsonsign".
The go tool cannot yet fetch from arbitrary domains, but discussion is
happening now on which mechanism to use to allow that.
For now, put the camlistore root under $GOPATH/src. Typically $GOPATH
is $HOME, so Camlistore should be at $HOME/src/camlistore.org.
Then you can:
$ go build ./server/camlistored
... etc
The build.pl script is currently disabled. It'll be resurrected at
some point, but with a very different role (helping create a fake
GOPATH and running the go build command, if things are installed at
the wrong place, and/or running fileembed generators).
Many things are certainly broken.
Many things are disabled. (MySQL, all indexing, etc).
Many things need to be moved into
camlistore.org/third_party/{code.google.com,github.com} and updated
from their r60 to Go 1 versions, where applicable.
The GoMySQL stuff should be updated to use database/sql and the ziutek
library implementing database/sql/driver.
Help wanted.
Change-Id: If71217dc5c8f0e70dbe46e9504ca5131c6eeacde
2012-02-19 05:53:06 +00:00
"log"
2011-01-17 05:16:26 +00:00
"os"
2011-03-16 06:16:24 +00:00
"path/filepath"
2014-05-07 13:17:07 +00:00
"strconv"
2010-12-20 22:54:41 +00:00
"strings"
2011-01-17 05:16:26 +00:00
"sync"
2011-04-02 05:26:33 +00:00
Update from r60 to [almost] Go 1.
A lot is still broken, but most stuff at least compiles now.
The directory tree has been rearranged now too. Go libraries are now
under "pkg". Fully qualified, they are e.g. "camlistore.org/pkg/jsonsign".
The go tool cannot yet fetch from arbitrary domains, but discussion is
happening now on which mechanism to use to allow that.
For now, put the camlistore root under $GOPATH/src. Typically $GOPATH
is $HOME, so Camlistore should be at $HOME/src/camlistore.org.
Then you can:
$ go build ./server/camlistored
... etc
The build.pl script is currently disabled. It'll be resurrected at
some point, but with a very different role (helping create a fake
GOPATH and running the go build command, if things are installed at
the wrong place, and/or running fileembed generators).
Many things are certainly broken.
Many things are disabled. (MySQL, all indexing, etc).
Many things need to be moved into
camlistore.org/third_party/{code.google.com,github.com} and updated
from their r60 to Go 1 versions, where applicable.
The GoMySQL stuff should be updated to use database/sql and the ziutek
library implementing database/sql/driver.
Help wanted.
Change-Id: If71217dc5c8f0e70dbe46e9504ca5131c6eeacde
2012-02-19 05:53:06 +00:00
"camlistore.org/pkg/auth"
2013-08-04 02:54:30 +00:00
"camlistore.org/pkg/blob"
2014-02-23 18:59:20 +00:00
"camlistore.org/pkg/buildinfo"
2013-10-26 18:08:34 +00:00
"camlistore.org/pkg/client/android"
2015-04-02 12:55:01 +00:00
"camlistore.org/pkg/env"
Update from r60 to [almost] Go 1.
A lot is still broken, but most stuff at least compiles now.
The directory tree has been rearranged now too. Go libraries are now
under "pkg". Fully qualified, they are e.g. "camlistore.org/pkg/jsonsign".
The go tool cannot yet fetch from arbitrary domains, but discussion is
happening now on which mechanism to use to allow that.
For now, put the camlistore root under $GOPATH/src. Typically $GOPATH
is $HOME, so Camlistore should be at $HOME/src/camlistore.org.
Then you can:
$ go build ./server/camlistored
... etc
The build.pl script is currently disabled. It'll be resurrected at
some point, but with a very different role (helping create a fake
GOPATH and running the go build command, if things are installed at
the wrong place, and/or running fileembed generators).
Many things are certainly broken.
Many things are disabled. (MySQL, all indexing, etc).
Many things need to be moved into
camlistore.org/third_party/{code.google.com,github.com} and updated
from their r60 to Go 1 versions, where applicable.
The GoMySQL stuff should be updated to use database/sql and the ziutek
library implementing database/sql/driver.
Help wanted.
Change-Id: If71217dc5c8f0e70dbe46e9504ca5131c6eeacde
2012-02-19 05:53:06 +00:00
"camlistore.org/pkg/jsonsign"
"camlistore.org/pkg/osutil"
2014-08-19 15:30:51 +00:00
"camlistore.org/pkg/types/camtypes"
2014-01-05 02:43:58 +00:00
"camlistore.org/pkg/types/clientconfig"
2015-12-01 16:19:49 +00:00
"go4.org/jsonconfig"
2015-11-26 23:22:35 +00:00
"go4.org/wkfs"
2010-12-20 22:54:41 +00:00
)
2014-03-28 18:45:22 +00:00
// If set, flagServer overrides the JSON config file
2013-12-07 14:48:02 +00:00
// ~/.config/camlistore/client-config.json
2014-03-28 18:45:22 +00:00
// (i.e. osutil.UserClientConfigPath()) "server" key.
2011-10-11 00:38:18 +00:00
//
2014-03-28 18:45:22 +00:00
// A main binary must call AddFlags to expose it.
var flagServer string
2011-10-11 00:38:18 +00:00
func AddFlags ( ) {
2014-02-23 18:59:20 +00:00
defaultPath := "/x/y/z/we're/in-a-test"
if ! buildinfo . TestingLinked ( ) {
defaultPath = osutil . UserClientConfigPath ( )
}
2013-04-25 22:25:34 +00:00
flag . StringVar ( & flagServer , "server" , "" , "Camlistore server prefix. If blank, the default from the \"server\" field of " + defaultPath + " is used. Acceptable forms: https://you.example.com, example.com:1345 (https assumed), or http://you.example.com/alt-root" )
2014-03-28 18:45:22 +00:00
osutil . AddSecretRingFlag ( )
2011-10-11 00:38:18 +00:00
}
2010-12-20 22:54:41 +00:00
2013-01-02 20:55:12 +00:00
// ExplicitServer returns the blobserver given in the flags, if any.
func ExplicitServer ( ) string {
2013-04-25 22:25:34 +00:00
return flagServer
2013-01-02 20:55:12 +00:00
}
2014-05-07 13:17:07 +00:00
var (
configOnce sync . Once
config * clientconfig . Config
configDisabled , _ = strconv . ParseBool ( os . Getenv ( "CAMLI_DISABLE_CLIENT_CONFIG_FILE" ) )
)
2013-06-21 14:10:40 +00:00
2014-06-23 23:57:06 +00:00
// config parsing in the global environment.
2013-12-10 16:52:52 +00:00
func parseConfig ( ) {
2014-06-23 23:57:06 +00:00
var nilClient * Client
nilClient . parseConfig ( )
}
// lazy config parsing when there's a known client already.
// The client c may be nil.
func ( c * Client ) parseConfig ( ) {
2013-12-10 16:52:52 +00:00
if android . OnAndroid ( ) {
2014-01-05 02:43:58 +00:00
panic ( "parseConfig should never have been called on Android" )
2013-12-10 16:52:52 +00:00
}
2014-05-07 13:17:07 +00:00
if configDisabled {
panic ( "parseConfig should never have been called with CAMLI_DISABLE_CLIENT_CONFIG_FILE set" )
}
2013-12-10 16:52:52 +00:00
configPath := osutil . UserClientConfigPath ( )
2014-08-08 18:02:06 +00:00
if _ , err := wkfs . Stat ( configPath ) ; os . IsNotExist ( err ) {
2014-06-23 23:57:06 +00:00
if c != nil && c . isSharePrefix {
return
}
2013-12-10 16:52:52 +00:00
errMsg := fmt . Sprintf ( "Client configuration file %v does not exist. See 'camput init' to generate it." , configPath )
if keyId := serverKeyId ( ) ; keyId != "" {
hint := fmt . Sprintf ( "\nThe key id %v was found in the server config %v, so you might want:\n'camput init -gpgkey %v'" , keyId , osutil . UserServerConfigPath ( ) , keyId )
errMsg += hint
}
log . Fatal ( errMsg )
}
2014-01-05 02:43:58 +00:00
// TODO: instead of using jsonconfig, we could read the file, and unmarshall into the structs that we now have in pkg/types/clientconfig. But we'll have to add the old fields (before the name changes, and before the multi-servers change) to the structs as well for our gracefull conversion/error messages to work.
2015-11-27 22:46:00 +00:00
conf , err := osutil . NewJSONConfigParser ( ) . ReadFile ( configPath )
2013-12-10 16:52:52 +00:00
if err != nil {
log . Fatal ( err . Error ( ) )
}
cfg := jsonconfig . Obj ( conf )
2014-01-05 02:43:58 +00:00
if singleServerAuth := cfg . OptionalString ( "auth" , "" ) ; singleServerAuth != "" {
newConf , err := convertToMultiServers ( cfg )
if err != nil {
log . Print ( err )
} else {
cfg = newConf
}
}
config = & clientconfig . Config {
Identity : cfg . OptionalString ( "identity" , "" ) ,
2014-03-28 18:45:22 +00:00
IdentitySecretRing : cfg . OptionalString ( "identitySecretRing" , "" ) ,
2014-01-05 02:43:58 +00:00
IgnoredFiles : cfg . OptionalList ( "ignoredFiles" ) ,
2013-12-10 16:52:52 +00:00
}
2014-01-05 02:43:58 +00:00
serversList := make ( map [ string ] * clientconfig . Server )
servers := cfg . OptionalObject ( "servers" )
for alias , vei := range servers {
// An alias should never be confused with a host name,
// so we forbid anything looking like one.
2014-01-07 06:14:46 +00:00
if isURLOrHostPort ( alias ) {
2014-01-06 18:46:07 +00:00
log . Fatalf ( "Server alias %q looks like a hostname; \".\" or \";\" are not allowed." , alias )
2014-01-05 02:43:58 +00:00
}
serverMap , ok := vei . ( map [ string ] interface { } )
if ! ok {
log . Fatalf ( "entry %q in servers section is a %T, want an object" , alias , vei )
}
serverConf := jsonconfig . Obj ( serverMap )
server := & clientconfig . Server {
Server : cleanServer ( serverConf . OptionalString ( "server" , "" ) ) ,
Auth : serverConf . OptionalString ( "auth" , "" ) ,
IsDefault : serverConf . OptionalBool ( "default" , false ) ,
TrustedCerts : serverConf . OptionalList ( "trustedCerts" ) ,
}
if err := serverConf . Validate ( ) ; err != nil {
log . Fatalf ( "Error in servers section of config file for server %q: %v" , alias , err )
}
serversList [ alias ] = server
}
config . Servers = serversList
2013-12-10 16:52:52 +00:00
if err := cfg . Validate ( ) ; err != nil {
printConfigChangeHelp ( cfg )
log . Fatalf ( "Error in config file: %v" , err )
}
}
2014-01-07 06:14:46 +00:00
// isURLOrHostPort returns true if s looks like a URL, or a hostname, i.e it starts with a scheme and/or it contains a period or a colon.
func isURLOrHostPort ( s string ) bool {
2014-01-05 02:43:58 +00:00
return strings . HasPrefix ( s , "http://" ) ||
strings . HasPrefix ( s , "https://" ) ||
strings . Contains ( s , "." ) || strings . Contains ( s , ":" )
}
// convertToMultiServers takes an old style single-server client configuration and maps it to new a multi-servers configuration that is returned.
func convertToMultiServers ( conf jsonconfig . Obj ) ( jsonconfig . Obj , error ) {
server := conf . OptionalString ( "server" , "" )
if server == "" {
return nil , errors . New ( "Could not convert config to multi-servers style: no \"server\" key found." )
}
newConf := jsonconfig . Obj {
"servers" : map [ string ] interface { } {
2014-01-16 00:27:47 +00:00
"server" : map [ string ] interface { } {
2014-01-05 02:43:58 +00:00
"auth" : conf . OptionalString ( "auth" , "" ) ,
"default" : true ,
"server" : server ,
} ,
} ,
"identity" : conf . OptionalString ( "identity" , "" ) ,
2014-03-28 18:45:22 +00:00
"identitySecretRing" : conf . OptionalString ( "identitySecretRing" , "" ) ,
2014-01-05 02:43:58 +00:00
}
if ignoredFiles := conf . OptionalList ( "ignoredFiles" ) ; ignoredFiles != nil {
var list [ ] interface { }
for _ , v := range ignoredFiles {
list = append ( list , v )
}
newConf [ "ignoredFiles" ] = list
}
return newConf , nil
}
2013-12-10 16:52:52 +00:00
// printConfigChangeHelp checks if conf contains obsolete keys,
// and prints additional help in this case.
func printConfigChangeHelp ( conf jsonconfig . Obj ) {
// rename maps from old key names to the new ones.
// If there is no new one, the value is the empty string.
rename := map [ string ] string {
"keyId" : "identity" ,
"publicKeyBlobref" : "" ,
"selfPubKeyDir" : "" ,
"secretRing" : "identitySecretRing" ,
}
oldConfig := false
configChangedMsg := fmt . Sprintf ( "The client configuration file (%s) keys have changed.\n" , osutil . UserClientConfigPath ( ) )
for _ , unknown := range conf . UnknownKeys ( ) {
2014-11-09 02:28:35 +00:00
v , ok := rename [ unknown ]
if ok {
if v != "" {
configChangedMsg += fmt . Sprintf ( "%q should be renamed %q.\n" , unknown , v )
} else {
configChangedMsg += fmt . Sprintf ( "%q should be removed.\n" , unknown )
2013-12-10 16:52:52 +00:00
}
2014-11-09 02:28:35 +00:00
oldConfig = true
2013-12-10 16:52:52 +00:00
}
}
if oldConfig {
configChangedMsg += "Please see http://camlistore.org/docs/client-config, or use camput init to recreate a default one."
log . Print ( configChangedMsg )
}
}
// serverKeyId returns the public gpg key id ("identity" field)
2013-06-21 14:10:40 +00:00
// from the user's server config , if any.
// It returns the empty string otherwise.
func serverKeyId ( ) string {
serverConfigFile := osutil . UserServerConfigPath ( )
2014-08-08 18:02:06 +00:00
if _ , err := wkfs . Stat ( serverConfigFile ) ; err != nil {
2013-06-21 14:10:40 +00:00
if os . IsNotExist ( err ) {
return ""
}
log . Fatalf ( "Could not stat %v: %v" , serverConfigFile , err )
}
obj , err := jsonconfig . ReadFile ( serverConfigFile )
if err != nil {
return ""
}
keyId , ok := obj [ "identity" ] . ( string )
if ! ok {
return ""
}
return keyId
}
2011-05-10 19:56:40 +00:00
2014-01-07 06:14:46 +00:00
// cleanServer returns the canonical URL of the provided server, which must be a URL, IP, host (with dot), or host/ip:port.
// The returned canonical URL will have trailing slashes removed and be prepended with "https://" if no scheme is provided.
2010-12-20 22:54:41 +00:00
func cleanServer ( server string ) string {
2014-01-07 06:14:46 +00:00
if ! isURLOrHostPort ( server ) {
2014-01-05 02:43:58 +00:00
log . Fatalf ( "server %q does not look like a server address and could be confused with a server alias. It should look like [http[s]://]foo[.com][:port] with at least one of the optional parts." , server )
}
2010-12-20 22:54:41 +00:00
// Remove trailing slash if provided.
if strings . HasSuffix ( server , "/" ) {
server = server [ 0 : len ( server ) - 1 ]
}
2011-11-16 10:41:38 +00:00
// Default to "https://" when not specified
if ! strings . HasPrefix ( server , "http" ) && ! strings . HasPrefix ( server , "https" ) {
server = "https://" + server
2010-12-20 22:54:41 +00:00
}
return server
}
2014-01-05 02:43:58 +00:00
// serverOrDie returns the server's URL found either as a command-line flag,
// or as the default server in the config file.
2013-01-26 00:23:31 +00:00
func serverOrDie ( ) string {
2014-01-05 02:43:58 +00:00
if s := os . Getenv ( "CAMLI_SERVER" ) ; s != "" {
return cleanServer ( s )
}
2013-04-25 22:25:34 +00:00
if flagServer != "" {
2014-01-07 06:14:46 +00:00
if ! isURLOrHostPort ( flagServer ) {
2014-01-05 02:43:58 +00:00
configOnce . Do ( parseConfig )
serverConf , ok := config . Servers [ flagServer ]
if ok {
return serverConf . Server
}
log . Printf ( "%q looks like a server alias, but no such alias found in config." , flagServer )
} else {
return cleanServer ( flagServer )
}
2010-12-20 22:54:41 +00:00
}
2014-01-05 02:43:58 +00:00
server := defaultServer ( )
2013-12-10 16:52:52 +00:00
if server == "" {
2014-08-19 15:30:51 +00:00
camtypes . ErrClientNoServer . Fatal ( )
2011-02-02 20:27:30 +00:00
}
2014-01-05 02:43:58 +00:00
return cleanServer ( server )
}
func defaultServer ( ) string {
configOnce . Do ( parseConfig )
2014-01-11 18:41:36 +00:00
wantAlias := os . Getenv ( "CAMLI_DEFAULT_SERVER" )
for alias , serverConf := range config . Servers {
if ( wantAlias != "" && wantAlias == alias ) || ( wantAlias == "" && serverConf . IsDefault ) {
2014-01-05 02:43:58 +00:00
return cleanServer ( serverConf . Server )
}
}
return ""
}
func ( c * Client ) serverOrDefault ( ) string {
configOnce . Do ( parseConfig )
if c . server != "" {
return cleanServer ( c . server )
}
return defaultServer ( )
2010-12-20 22:54:41 +00:00
}
2013-04-08 13:50:50 +00:00
func ( c * Client ) useTLS ( ) bool {
2015-09-25 14:27:43 +00:00
return strings . HasPrefix ( c . discoRoot ( ) , "https://" )
2013-04-08 13:50:50 +00:00
}
2014-01-08 03:21:24 +00:00
// SetupAuth sets the client's authMode. It tries from the environment first if we're on android or in dev mode, and then from the client configuration.
Update from r60 to [almost] Go 1.
A lot is still broken, but most stuff at least compiles now.
The directory tree has been rearranged now too. Go libraries are now
under "pkg". Fully qualified, they are e.g. "camlistore.org/pkg/jsonsign".
The go tool cannot yet fetch from arbitrary domains, but discussion is
happening now on which mechanism to use to allow that.
For now, put the camlistore root under $GOPATH/src. Typically $GOPATH
is $HOME, so Camlistore should be at $HOME/src/camlistore.org.
Then you can:
$ go build ./server/camlistored
... etc
The build.pl script is currently disabled. It'll be resurrected at
some point, but with a very different role (helping create a fake
GOPATH and running the go build command, if things are installed at
the wrong place, and/or running fileembed generators).
Many things are certainly broken.
Many things are disabled. (MySQL, all indexing, etc).
Many things need to be moved into
camlistore.org/third_party/{code.google.com,github.com} and updated
from their r60 to Go 1 versions, where applicable.
The GoMySQL stuff should be updated to use database/sql and the ziutek
library implementing database/sql/driver.
Help wanted.
Change-Id: If71217dc5c8f0e70dbe46e9504ca5131c6eeacde
2012-02-19 05:53:06 +00:00
func ( c * Client ) SetupAuth ( ) error {
2014-05-07 13:17:07 +00:00
if c . paramsOnly {
if c . authMode != nil {
if _ , ok := c . authMode . ( * auth . None ) ; ! ok {
return nil
}
}
return errors . New ( "client: paramsOnly set; auth should not be configured from config or env vars." )
}
2014-01-08 03:21:24 +00:00
// env var takes precedence, but only if we're in dev mode or on android.
// Too risky otherwise.
2014-05-07 13:17:07 +00:00
if android . OnAndroid ( ) ||
2015-04-02 12:55:01 +00:00
env . IsDev ( ) ||
2014-05-07 13:17:07 +00:00
configDisabled {
2014-01-08 03:21:24 +00:00
authMode , err := auth . FromEnv ( )
if err == nil {
c . authMode = authMode
return nil
}
if err != auth . ErrNoAuth {
return fmt . Errorf ( "Could not set up auth from env var CAMLI_AUTH: %v" , err )
}
2014-01-05 02:43:58 +00:00
}
if c . server == "" {
2014-01-08 03:21:24 +00:00
return fmt . Errorf ( "No server defined for this client: can not set up auth." )
2013-12-10 16:52:52 +00:00
}
2014-01-05 02:43:58 +00:00
authConf := serverAuth ( c . server )
if authConf == "" {
2014-02-26 18:18:09 +00:00
c . authErr = fmt . Errorf ( "could not find auth key for server %q in config, defaulting to no auth" , c . server )
c . authMode = auth . None { }
return nil
2013-12-10 16:52:52 +00:00
}
2014-01-08 03:21:24 +00:00
var err error
2014-01-05 02:43:58 +00:00
c . authMode , err = auth . FromConfig ( authConf )
2013-12-10 16:52:52 +00:00
return err
2011-11-16 10:41:38 +00:00
}
2014-01-07 06:14:46 +00:00
// serverAuth returns the auth scheme for server from the config, or the empty string if the server was not found in the config.
2014-01-05 02:43:58 +00:00
func serverAuth ( server string ) string {
configOnce . Do ( parseConfig )
2014-01-07 06:14:46 +00:00
alias := config . Alias ( server )
if alias == "" {
2014-01-05 02:43:58 +00:00
return ""
}
2014-01-07 06:14:46 +00:00
return config . Servers [ alias ] . Auth
2014-01-05 02:43:58 +00:00
}
2013-12-26 08:48:29 +00:00
// SetupAuthFromString configures the clients authentication mode from
// an explicit auth string.
func ( c * Client ) SetupAuthFromString ( a string ) error {
2014-01-05 02:43:58 +00:00
// TODO(mpl): review the one using that (pkg/blobserver/remote/remote.go)
2012-11-08 03:11:12 +00:00
var err error
2013-12-26 08:48:29 +00:00
c . authMode , err = auth . FromConfig ( a )
2011-12-02 10:35:28 +00:00
return err
2010-12-20 22:54:41 +00:00
}
2013-07-11 06:46:23 +00:00
// SecretRingFile returns the filename to the user's GPG secret ring.
2014-03-28 18:45:22 +00:00
// The value comes from either the --secret-keyring flag, the
2013-12-10 16:52:52 +00:00
// CAMLI_SECRET_RING environment variable, the client config file's
// "identitySecretRing" value, or the operating system default location.
2011-05-30 19:38:26 +00:00
func ( c * Client ) SecretRingFile ( ) string {
2014-03-28 18:45:22 +00:00
if secretRing , ok := osutil . ExplicitSecretRingFile ( ) ; ok {
return secretRing
2012-04-13 02:36:00 +00:00
}
2014-01-05 02:43:58 +00:00
if android . OnAndroid ( ) {
2014-03-28 18:45:22 +00:00
panic ( "on android, so CAMLI_SECRET_RING should have been defined, or --secret-keyring used." )
2014-01-05 02:43:58 +00:00
}
2014-05-07 13:17:07 +00:00
if c . paramsOnly {
log . Print ( "client: paramsOnly set; cannot get secret ring file from config or env vars." )
return ""
}
if configDisabled {
panic ( "Need a secret ring, and config file disabled" )
}
2013-12-10 16:52:52 +00:00
configOnce . Do ( parseConfig )
2014-01-05 02:43:58 +00:00
if config . IdentitySecretRing == "" {
2014-03-28 18:45:22 +00:00
return osutil . SecretRingFile ( )
2014-01-04 00:07:10 +00:00
}
2014-01-05 02:43:58 +00:00
return config . IdentitySecretRing
2011-05-30 19:38:26 +00:00
}
2012-04-13 02:36:00 +00:00
func fileExists ( name string ) bool {
_ , err := os . Stat ( name )
return err == nil
}
2013-12-10 16:52:52 +00:00
// SignerPublicKeyBlobref returns the blobref of signer's public key.
// The blobref may not be valid (zero blob.Ref) if e.g the configuration
// is invalid or incomplete.
func ( c * Client ) SignerPublicKeyBlobref ( ) blob . Ref {
c . initSignerPublicKeyBlobrefOnce . Do ( c . initSignerPublicKeyBlobref )
return c . signerPublicKeyRef
2012-08-04 23:25:21 +00:00
}
2013-12-10 16:52:52 +00:00
func ( c * Client ) initSignerPublicKeyBlobref ( ) {
2014-05-07 13:17:07 +00:00
if c . paramsOnly {
log . Print ( "client: paramsOnly set; cannot get public key from config or env vars." )
return
}
2014-03-31 14:30:54 +00:00
keyId := os . Getenv ( "CAMLI_KEYID" )
2013-12-10 16:52:52 +00:00
if keyId == "" {
2014-01-20 21:54:42 +00:00
configOnce . Do ( parseConfig )
keyId = config . Identity
if keyId == "" {
log . Fatalf ( "No 'identity' key in JSON configuration file %q; have you run \"camput init\"?" , osutil . UserClientConfigPath ( ) )
}
2011-01-17 05:16:26 +00:00
}
2013-12-10 16:52:52 +00:00
keyRing := c . SecretRingFile ( )
if ! fileExists ( keyRing ) {
log . Fatalf ( "Could not find keyId %q, because secret ring file %q does not exist." , keyId , keyRing )
2012-04-13 02:36:00 +00:00
}
2011-05-30 19:38:26 +00:00
entity , err := jsonsign . EntityFromSecring ( keyId , keyRing )
if err != nil {
2013-12-10 16:52:52 +00:00
log . Fatalf ( "Couldn't find keyId %q in secret ring %v: %v" , keyId , keyRing , err )
2011-05-30 19:38:26 +00:00
}
2013-12-10 16:52:52 +00:00
armored , err := jsonsign . ArmoredPublicKey ( entity )
2011-05-30 19:38:26 +00:00
if err != nil {
2013-12-10 16:52:52 +00:00
log . Fatalf ( "Error serializing public key: %v" , err )
2011-05-30 19:38:26 +00:00
}
2014-01-20 21:47:08 +00:00
c . signerPublicKeyRef = blob . SHA1FromString ( armored )
2013-12-10 16:52:52 +00:00
c . publicKeyArmored = armored
2011-01-17 05:16:26 +00:00
}
2013-04-08 13:50:50 +00:00
func ( c * Client ) initTrustedCerts ( ) {
2014-05-07 13:17:07 +00:00
if c . paramsOnly {
return
}
2013-04-08 13:50:50 +00:00
if e := os . Getenv ( "CAMLI_TRUSTED_CERT" ) ; e != "" {
2013-09-06 04:07:15 +00:00
c . trustedCerts = strings . Split ( e , "," )
2013-04-08 13:50:50 +00:00
return
}
c . trustedCerts = [ ] string { }
2014-05-07 13:17:07 +00:00
if android . OnAndroid ( ) || configDisabled {
2013-04-08 13:50:50 +00:00
return
}
2014-01-05 02:43:58 +00:00
if c . server == "" {
log . Printf ( "No server defined: can not define trustedCerts for this client." )
return
}
2014-06-23 23:57:06 +00:00
trustedCerts := c . serverTrustedCerts ( c . server )
2014-01-05 02:43:58 +00:00
if trustedCerts == nil {
2013-04-08 13:50:50 +00:00
return
}
2014-01-05 02:43:58 +00:00
for _ , trustedCert := range trustedCerts {
2013-04-08 13:50:50 +00:00
c . trustedCerts = append ( c . trustedCerts , strings . ToLower ( trustedCert ) )
}
}
2014-01-07 06:14:46 +00:00
// serverTrustedCerts returns the trusted certs for server from the config.
2014-06-23 23:57:06 +00:00
func ( c * Client ) serverTrustedCerts ( server string ) [ ] string {
configOnce . Do ( c . parseConfig )
if config == nil {
return nil
}
2014-01-07 06:14:46 +00:00
alias := config . Alias ( server )
if alias == "" {
return nil
2014-01-05 02:43:58 +00:00
}
2014-01-07 06:14:46 +00:00
return config . Servers [ alias ] . TrustedCerts
2014-01-05 02:43:58 +00:00
}
2013-12-10 16:52:52 +00:00
func ( c * Client ) getTrustedCerts ( ) [ ] string {
2013-08-23 19:30:01 +00:00
c . initTrustedCertsOnce . Do ( c . initTrustedCerts )
2013-04-08 13:50:50 +00:00
return c . trustedCerts
}
2013-08-19 15:17:35 +00:00
func ( c * Client ) initIgnoredFiles ( ) {
2013-12-27 17:40:23 +00:00
defer func ( ) {
c . ignoreChecker = newIgnoreChecker ( c . ignoredFiles )
} ( )
2014-05-07 13:17:07 +00:00
if c . paramsOnly {
return
}
2013-08-19 15:17:35 +00:00
if e := os . Getenv ( "CAMLI_IGNORED_FILES" ) ; e != "" {
2013-09-06 04:07:15 +00:00
c . ignoredFiles = strings . Split ( e , "," )
2013-08-19 15:17:35 +00:00
return
}
c . ignoredFiles = [ ] string { }
2014-05-07 13:17:07 +00:00
if android . OnAndroid ( ) || configDisabled {
2013-08-19 15:17:35 +00:00
return
}
2014-01-05 02:43:58 +00:00
configOnce . Do ( parseConfig )
c . ignoredFiles = config . IgnoredFiles
2013-08-19 15:17:35 +00:00
}
2014-02-23 17:34:16 +00:00
var osutilHomeDir = osutil . HomeDir // changed by tests
2013-12-27 17:40:23 +00:00
// newIgnoreChecker uses ignoredFiles to build and return a func that returns whether the file path argument should be ignored. See IsIgnoredFile for the ignore rules.
func newIgnoreChecker ( ignoredFiles [ ] string ) func ( path string ) ( shouldIgnore bool ) {
var fns [ ] func ( string ) bool
// copy of ignoredFiles for us to mutate
ignFiles := append ( [ ] string ( nil ) , ignoredFiles ... )
for k , v := range ignFiles {
if strings . HasPrefix ( v , filepath . FromSlash ( "~/" ) ) {
2014-02-23 18:59:20 +00:00
ignFiles [ k ] = filepath . Join ( osutilHomeDir ( ) , v [ 2 : ] )
2013-12-27 17:40:23 +00:00
}
}
// We cache the ignoredFiles patterns in 3 categories (not necessarily exclusive):
// 1) shell patterns
// 3) absolute paths
// 4) paths components
for _ , pattern := range ignFiles {
2014-03-04 14:29:20 +00:00
pattern := pattern
2013-12-27 17:40:23 +00:00
_ , err := filepath . Match ( pattern , "whatever" )
if err == nil {
fns = append ( fns , func ( v string ) bool { return isShellPatternMatch ( pattern , v ) } )
}
}
for _ , pattern := range ignFiles {
2014-03-04 14:29:20 +00:00
pattern := pattern
2013-12-27 17:40:23 +00:00
if filepath . IsAbs ( pattern ) {
fns = append ( fns , func ( v string ) bool { return hasDirPrefix ( filepath . Clean ( pattern ) , v ) } )
} else {
fns = append ( fns , func ( v string ) bool { return hasComponent ( filepath . Clean ( pattern ) , v ) } )
}
}
return func ( path string ) bool {
for _ , fn := range fns {
if fn ( path ) {
return true
}
}
return false
}
}
var filepathSeparatorString = string ( filepath . Separator )
// isShellPatternMatch returns whether fullpath matches the shell pattern, as defined by http://golang.org/pkg/path/filepath/#Match. As an additional special case, when the pattern looks like a basename, the last path element of fullpath is also checked against it.
func isShellPatternMatch ( shellPattern , fullpath string ) bool {
match , _ := filepath . Match ( shellPattern , fullpath )
if match {
return true
}
if ! strings . Contains ( shellPattern , filepathSeparatorString ) {
match , _ := filepath . Match ( shellPattern , filepath . Base ( fullpath ) )
if match {
return true
}
}
return false
}
// hasDirPrefix reports whether the path has the provided directory prefix.
// Both should be absolute paths.
func hasDirPrefix ( dirPrefix , fullpath string ) bool {
if ! strings . HasPrefix ( fullpath , dirPrefix ) {
return false
}
if len ( fullpath ) == len ( dirPrefix ) {
return true
}
if fullpath [ len ( dirPrefix ) ] == filepath . Separator {
return true
}
return false
}
2016-01-06 16:44:08 +00:00
// hasComponent returns whether the pathComponent is a path component of
// fullpath. i.e it is a part of fullpath that fits exactly between two path
// separators.
2013-12-27 17:40:23 +00:00
func hasComponent ( component , fullpath string ) bool {
2016-01-06 16:44:08 +00:00
// trim Windows volume name
fullpath = strings . TrimPrefix ( fullpath , filepath . VolumeName ( fullpath ) )
for {
i := strings . Index ( fullpath , component )
if i == - 1 {
return false
}
if i != 0 && fullpath [ i - 1 ] == filepath . Separator {
componentEnd := i + len ( component )
if componentEnd == len ( fullpath ) {
return true
}
if fullpath [ componentEnd ] == filepath . Separator {
return true
}
}
fullpath = fullpath [ i + 1 : ]
2013-12-27 17:40:23 +00:00
}
2016-01-06 16:44:08 +00:00
panic ( "unreachable" )
2013-08-19 15:17:35 +00:00
}