diff --git a/pkg/client/client.go b/pkg/client/client.go index 82064ae0d..32a7b42b6 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -948,7 +948,7 @@ func (c *Client) signerInit() { func (c *Client) buildSigner() (*schema.Signer, error) { c.initSignerPublicKeyBlobrefOnce.Do(c.initSignerPublicKeyBlobref) if !c.signerPublicKeyRef.Valid() { - return nil, camtypes.Err("client-no-public-key") + return nil, camtypes.ErrClientNoPublicKey } return schema.NewSigner(c.signerPublicKeyRef, strings.NewReader(c.publicKeyArmored), c.SecretRingFile()) } diff --git a/pkg/client/config.go b/pkg/client/config.go index b7220d11f..1660cc602 100644 --- a/pkg/client/config.go +++ b/pkg/client/config.go @@ -34,6 +34,7 @@ import ( "camlistore.org/pkg/jsonconfig" "camlistore.org/pkg/jsonsign" "camlistore.org/pkg/osutil" + "camlistore.org/pkg/types/camtypes" "camlistore.org/pkg/types/clientconfig" "camlistore.org/pkg/wkfs" ) @@ -270,7 +271,7 @@ func serverOrDie() string { } server := defaultServer() if server == "" { - log.Fatalf("No valid server defined with CAMLI_SERVER, or with -server, or in %q", osutil.UserClientConfigPath()) + camtypes.ErrClientNoServer.Fatal() } return cleanServer(server) } diff --git a/pkg/types/camtypes/errors.go b/pkg/types/camtypes/errors.go index ae89e7bc3..46564cec7 100644 --- a/pkg/types/camtypes/errors.go +++ b/pkg/types/camtypes/errors.go @@ -18,16 +18,19 @@ package camtypes import ( "fmt" + "log" + + "camlistore.org/pkg/osutil" ) //TODO(mpl): move pkg/camerrors stuff in here -var camErrors = map[string]error{} +var camErrors = map[string]*camErr{} -func init() { - // TODO(mpl): set des to be "See http://camlistore.org/err/client-no-public-key" - addCamError("client-no-public-key", "No public key configured: see 'camput init'.") -} +var ( + ErrClientNoServer = addCamError("client-no-server", fmt.Sprintf("No valid server defined. It can be set with the CAMLI_SERVER environment variable, or the --server flag, or in the \"servers\" section of %q (see https://camlistore.org/docs/client-config).", osutil.UserClientConfigPath())) + ErrClientNoPublicKey = addCamError("client-no-public-key", "No public key configured: see 'camput init'.") +) type camErr struct { key string @@ -38,6 +41,18 @@ func (ce *camErr) Error() string { return ce.des } +func (ce *camErr) Fatal() { + log.Fatalf("%v error. See %v", ce.key, ce.URL()) +} + +func (ce *camErr) Warn() { + log.Printf("%v error. See %v.", ce.key, ce.URL()) +} + +func (ce *camErr) URL() string { + return fmt.Sprintf("https://camlistore.org/err/%s", ce.key) +} + // Err returns the error registered for key. // It panics for an unregistered key. func Err(key string) error { @@ -48,9 +63,14 @@ func Err(key string) error { return v } -func addCamError(key, des string) { - camErrors[key] = &camErr{ +func addCamError(key, des string) *camErr { + if e, ok := camErrors[key]; ok { + panic(fmt.Sprintf("error %v already registered as %q", key, e.Error())) + } + e := &camErr{ key: key, des: des, } + camErrors[key] = e + return e } diff --git a/website/camweb.go b/website/camweb.go index dcdb65f95..dd3f593ed 100644 --- a/website/camweb.go +++ b/website/camweb.go @@ -465,7 +465,12 @@ func ipHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(str)) } -const errPattern = "/err/" +const ( + errPattern = "/err/" + toHyperlink = `$1$2` +) + +var camliURLPattern = regexp.MustCompile(`(https?://camlistore.org)([a-zA-Z0-9\-\_/]+)?`) func errHandler(w http.ResponseWriter, r *http.Request) { errString := strings.TrimPrefix(r.URL.Path, errPattern) @@ -478,10 +483,10 @@ func errHandler(w http.ResponseWriter, r *http.Request) { err := camtypes.Err(errString) data := struct { Code string - Description string + Description template.HTML }{ Code: errString, - Description: err.Error(), + Description: template.HTML(camliURLPattern.ReplaceAllString(err.Error(), toHyperlink)), } contents := applyTemplate(camliErrorHTML, "camliErrorHTML", data) w.WriteHeader(http.StatusFound)