2015-02-21 12:22:11 +00:00
/ *
Copyright 2015 The Camlistore Authors .
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 .
* /
package server
import (
"encoding/json"
2015-10-20 15:59:38 +00:00
"errors"
2015-02-21 12:22:11 +00:00
"fmt"
"html/template"
"net/http"
2015-09-28 14:25:40 +00:00
"strconv"
2015-10-20 15:59:38 +00:00
"strings"
2015-02-21 12:22:11 +00:00
"camlistore.org/pkg/blobserver"
"camlistore.org/pkg/httputil"
"camlistore.org/pkg/types/clientconfig"
2015-12-01 16:19:49 +00:00
"go4.org/jsonconfig"
2015-02-21 12:22:11 +00:00
)
const helpHTML string = ` < html >
< head >
< title > Help < / title >
< / head >
< body >
< h2 > Help < / h2 >
2015-06-29 15:54:17 +00:00
< h3 > Web User Interface < / h3 >
2016-04-27 05:12:47 +00:00
< p > < a href = ' https : //camlistore.org/doc/search-ui'>Search bar predicates.</a></p>
2015-06-29 15:54:17 +00:00
2015-07-21 18:59:18 +00:00
< h3 > Client tools < / h3 >
< p >
2015-12-30 18:33:25 +00:00
You can download the Camlistore command line tools for Linux , Mac , and Windows at :
2015-07-21 18:59:18 +00:00
< ul >
2015-12-30 18:33:25 +00:00
< li > < a href = "https://camlistore.org/download" > camlistore . org / download < / a > < / li >
2015-07-21 18:59:18 +00:00
< / ul >
< / p >
2016-04-27 15:48:50 +00:00
< p > You will need to use the following < a href = ' https : //camlistore.org/doc/client-config'>client configuration</a> in order to access this server using the command line tools.</p>
2015-12-30 18:33:25 +00:00
< pre > { { . ClientConfigJSON } } < / pre >
{ { . SecringDownloadHint } }
2015-06-29 15:54:17 +00:00
< h3 > Anything Else ? < / h3 >
2017-10-17 05:47:54 +00:00
< p > See the Camlistore < a href = ' https : //camlistore.org/doc/'>online documentation</a> and <a href='https://camlistore.org/community'>community contacts</a>.</p>
2015-02-21 12:22:11 +00:00
< / body >
< / html > `
// HelpHandler publishes information related to accessing the server
type HelpHandler struct {
2015-12-30 18:33:25 +00:00
clientConfig * clientconfig . Config // generated from serverConfig
serverConfig jsonconfig . Obj // low-level config
goTemplate * template . Template // for rendering
serverSecRing string
2015-02-21 12:22:11 +00:00
}
// SetServerConfig enables the handler to receive the server config
// before InitHandler, which generates a client config from the server config, is called.
func ( hh * HelpHandler ) SetServerConfig ( config jsonconfig . Obj ) {
2015-12-30 18:33:25 +00:00
if hh . serverConfig == nil {
hh . serverConfig = config
}
2015-02-21 12:22:11 +00:00
}
func init ( ) {
blobserver . RegisterHandlerConstructor ( "help" , newHelpFromConfig )
}
2015-10-20 15:59:38 +00:00
// fixServerInConfig checks if cc contains a meaningful server (for a client).
// If not, a newly allocated clone of cc is returned, except req.Host is used for
// the hostname of the server. Otherwise, cc is returned.
func fixServerInConfig ( cc * clientconfig . Config , req * http . Request ) ( * clientconfig . Config , error ) {
if cc == nil {
return nil , errors . New ( "nil client config" )
}
if len ( cc . Servers ) == 0 || cc . Servers [ "default" ] == nil || cc . Servers [ "default" ] . Server == "" {
return nil , errors . New ( "no Server in client config" )
}
listen := strings . TrimPrefix ( strings . TrimPrefix ( cc . Servers [ "default" ] . Server , "http://" ) , "https://" )
if ! ( strings . HasPrefix ( listen , "0.0.0.0" ) || strings . HasPrefix ( listen , ":" ) ) {
return cc , nil
}
newCC := * cc
server := newCC . Servers [ "default" ]
if req . TLS != nil {
server . Server = "https://" + req . Host
} else {
server . Server = "http://" + req . Host
}
newCC . Servers [ "default" ] = server
return & newCC , nil
}
2015-02-21 12:22:11 +00:00
func ( hh * HelpHandler ) InitHandler ( hl blobserver . FindHandlerByTyper ) error {
if hh . serverConfig == nil {
return fmt . Errorf ( "HelpHandler's serverConfig must be set before calling its InitHandler" )
}
clientConfig , err := clientconfig . GenerateClientConfig ( hh . serverConfig )
if err != nil {
return fmt . Errorf ( "error generating client config: %v" , err )
}
hh . clientConfig = clientConfig
2015-12-30 18:33:25 +00:00
hh . serverSecRing = clientConfig . IdentitySecretRing
clientConfig . IdentitySecretRing = "/home/you/.config/camlistore/identity-secring.gpg"
2015-02-21 12:22:11 +00:00
tmpl , err := template . New ( "help" ) . Parse ( helpHTML )
if err != nil {
return fmt . Errorf ( "error creating template: %v" , err )
}
hh . goTemplate = tmpl
return nil
}
func newHelpFromConfig ( ld blobserver . Loader , conf jsonconfig . Obj ) ( h http . Handler , err error ) {
return & HelpHandler { } , nil
}
func ( hh * HelpHandler ) ServeHTTP ( rw http . ResponseWriter , req * http . Request ) {
suffix := httputil . PathSuffix ( req )
if ! httputil . IsGet ( req ) {
http . Error ( rw , "Illegal help method." , http . StatusMethodNotAllowed )
return
}
switch suffix {
case "" :
2015-10-20 15:59:38 +00:00
cc , err := fixServerInConfig ( hh . clientConfig , req )
if err != nil {
httputil . ServeError ( rw , req , err )
return
}
2015-09-28 14:25:40 +00:00
if clientConfig := req . FormValue ( "clientConfig" ) ; clientConfig != "" {
if clientConfigOnly , err := strconv . ParseBool ( clientConfig ) ; err == nil && clientConfigOnly {
2015-10-20 15:59:38 +00:00
httputil . ReturnJSON ( rw , cc )
2015-09-28 14:25:40 +00:00
return
}
}
2015-10-20 15:59:38 +00:00
hh . serveHelpHTML ( cc , rw , req )
2015-02-21 12:22:11 +00:00
default :
http . Error ( rw , "Illegal help path." , http . StatusNotFound )
}
}
2015-10-20 15:59:38 +00:00
func ( hh * HelpHandler ) serveHelpHTML ( cc * clientconfig . Config , rw http . ResponseWriter , req * http . Request ) {
jsonBytes , err := json . MarshalIndent ( cc , "" , " " )
2015-02-21 12:22:11 +00:00
if err != nil {
httputil . ServeError ( rw , req , fmt . Errorf ( "could not serialize client config JSON: %v" , err ) )
return
}
2015-12-30 18:33:25 +00:00
var hint template . HTML
if strings . HasPrefix ( hh . serverSecRing , "/gcs/" ) {
bucketdir := strings . TrimPrefix ( hh . serverSecRing , "/gcs/" )
bucketdir = strings . TrimSuffix ( bucketdir , "/identity-secring.gpg" )
2016-04-27 15:48:50 +00:00
hint = template . HTML ( fmt . Sprintf ( "<p>Download your GnuPG secret ring from <a href=\"https://console.developers.google.com/storage/browser/%s/\">https://console.developers.google.com/storage/browser/%s/</a> and place it in your <a href='https://camlistore.org/doc/client-config'>Camlistore client config directory</a>. Keep it private. It's not encrypted or password-protected and anybody in possession of it can create Camlistore claims as your identity.</p>\n" ,
2015-12-30 18:33:25 +00:00
bucketdir , bucketdir ) )
}
hh . goTemplate . Execute ( rw , struct {
ClientConfigJSON string
SecringDownloadHint template . HTML
} {
ClientConfigJSON : string ( jsonBytes ) ,
SecringDownloadHint : hint ,
} )
2015-02-21 12:22:11 +00:00
}