示例#1
0
func handleSetupChange(rw http.ResponseWriter, req *http.Request) {
	hilevelConf, err := jsonconfig.ReadFile(osutil.UserServerConfigPath())
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}
	if !xsrftoken.Valid(req.FormValue("token"), serverKey, "user", "wizardSave") {
		http.Error(rw, "Form expired. Press back and reload form.", http.StatusBadRequest)
		log.Printf("invalid xsrf token=%q", req.FormValue("token"))
		return
	}

	hasChanged := false
	var el interface{}
	publish := jsonconfig.Obj{}
	for k, v := range req.Form {
		if _, ok := hilevelConf[k]; !ok {
			if k != "gallery" && k != "blog" {
				continue
			}
		}

		switch k {
		case "https", "shareHandler":
			b, err := strconv.ParseBool(v[0])
			if err != nil {
				httputil.ServeError(rw, req, fmt.Errorf("%v field expects a boolean value", k))
			}
			el = b
		default:
			el = v[0]
		}
		if reflect.DeepEqual(hilevelConf[k], el) {
			continue
		}
		hasChanged = true
		hilevelConf[k] = el
	}
	// "publish" wasn't checked yet
	if !reflect.DeepEqual(hilevelConf["publish"], publish) {
		hilevelConf["publish"] = publish
		hasChanged = true
	}

	if hasChanged {
		err = rewriteConfig(&hilevelConf, osutil.UserServerConfigPath())
		if err != nil {
			httputil.ServeError(rw, req, err)
			return
		}
		err = osutil.RestartProcess()
		if err != nil {
			log.Fatal("Failed to restart: " + err.Error())
			http.Error(rw, "Failed to restart process", 500)
			return
		}
	}
	sendWizard(rw, req, hasChanged)
}
示例#2
0
func TestStarts(t *testing.T) {
	td, err := ioutil.TempDir("", "camlistored-test")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(td)

	fakeHome := filepath.Join(td, "fakeHome")
	confDir := filepath.Join(fakeHome, "conf")

	defer pushEnv("HOME", fakeHome)()
	defer pushEnv("HOMEPATH", fakeHome)()
	defer pushEnv("APPDATA", filepath.Join(fakeHome, "appdata"))()
	defer pushEnv("CAMLI_CONFIG_DIR", confDir)()

	if _, err := os.Stat(osutil.CamliConfigDir()); !os.IsNotExist(err) {
		t.Fatalf("expected conf dir %q to not exist", osutil.CamliConfigDir())
	}
	if !strings.Contains(osutil.CamliBlobRoot(), td) {
		t.Fatalf("blob root %q should contain the temp dir %q", osutil.CamliBlobRoot(), td)
	}
	if _, err := os.Stat(osutil.CamliBlobRoot()); !os.IsNotExist(err) {
		t.Fatalf("expected blobroot dir %q to not exist", osutil.CamliBlobRoot())
	}
	if fi, err := os.Stat(osutil.UserServerConfigPath()); !os.IsNotExist(err) {
		t.Errorf("expected no server config file; got %v, %v", fi, err)
	}

	mkdir(t, confDir)
	*flagOpenBrowser = false
	defaultListenAddr = ":0"

	up := make(chan struct{})
	down := make(chan struct{})
	dead := make(chan int, 1)
	osExit = func(status int) {
		dead <- status
		close(dead)
		runtime.Goexit()
	}
	go Main(up, down)
	select {
	case status := <-dead:
		t.Errorf("os.Exit(%d) before server came up", status)
		return
	case <-up:
		t.Logf("server is up")
	case <-time.After(10 * time.Second):
		t.Fatal("timeout starting server")
	}

	if _, err := os.Stat(osutil.UserServerConfigPath()); err != nil {
		t.Errorf("expected a server config file; got %v", err)
	}

	down <- struct{}{}
	<-dead
}
示例#3
0
func (c *reindexdpCmd) RunCommand(args []string) error {
	var path string
	switch {
	case len(args) == 0:
		cfg, err := serverconfig.Load(osutil.UserServerConfigPath())
		if err != nil {
			return err
		}
		prefixes, ok := cfg.Obj["prefixes"].(map[string]interface{})
		if !ok {
			return fmt.Errorf("No 'prefixes' object in low-level (or converted) config file %s", osutil.UserServerConfigPath())
		}
		paths := []string{}
		for prefix, vei := range prefixes {
			pmap, ok := vei.(map[string]interface{})
			if !ok {
				log.Printf("prefix %q value is a %T, not an object", prefix, vei)
				continue
			}
			pconf := jsonconfig.Obj(pmap)
			handlerType := pconf.RequiredString("handler")
			handlerArgs := pconf.OptionalObject("handlerArgs")
			// no pconf.Validate, as this is a recover tool
			if handlerType != "storage-diskpacked" {
				continue
			}
			if handlerArgs == nil {
				log.Printf("no handlerArgs for %q", prefix)
				continue
			}
			aconf := jsonconfig.Obj(handlerArgs)
			path = aconf.RequiredString("path")
			// no aconv.Validate, as this is a recover tool
			if path != "" {
				paths = append(paths, path)
			}
		}
		if len(paths) == 0 {
			return fmt.Errorf("Server config file %s doesn't specify a disk-packed storage handler.",
				osutil.UserServerConfigPath())
		}
		if len(paths) > 1 {
			return fmt.Errorf("Ambiguity. Server config file %s d specify more than 1 disk-packed storage handler. Please specify one of: %v", osutil.UserServerConfigPath(), paths)
		}
		path = paths[0]
	case len(args) == 1:
		path = args[0]
	default:
		return errors.New("More than 1 argument not allowed")
	}
	if path == "" {
		return errors.New("no path is given/found")
	}

	return diskpacked.Reindex(path, c.overwrite)
}
示例#4
0
func sendWizard(rw http.ResponseWriter, req *http.Request, hasChanged bool) {
	config, err := jsonconfig.ReadFile(osutil.UserServerConfigPath())
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}

	err = flattenPublish(config)
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}

	funcMap := template.FuncMap{
		"printWizard": printWizard,
		"showField": func(inputName string) bool {
			if _, ok := ignoredFields[inputName]; ok {
				return false
			}
			return true
		},
		"genXSRF": func() string {
			return xsrftoken.Generate(serverKey, "user", "wizardSave")
		},
	}

	body := `
	<form id="WizardForm" method="POST" enctype="multipart/form-data">
	<table>
	{{range $k,$v := .}}{{if showField $k}}<tr><td>{{printf "%v" $k}}</td><td><input type="text" size="30" name ="{{printf "%v" $k}}" value="{{printWizard $v}}" ></td></tr>{{end}}{{end}}
	</table>
	<input type="hidden" name="token" value="{{genXSRF}}">
	<input type="submit" form="WizardForm" value="Save"> (Will restart server.)</form>`

	if hasChanged {
		body += `<p> Configuration succesfully rewritten </p>`
	}

	tmpl, err := template.New("wizard").Funcs(funcMap).Parse(topWizard + body + bottomWizard)
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}
	err = tmpl.Execute(rw, config)
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}
}
示例#5
0
func (c *reindexdpCmd) RunCommand(args []string) error {
	var path string
	switch {
	case len(args) == 0:
		cfg, err := serverconfig.Load(osutil.UserServerConfigPath())
		if err != nil {
			return err
		}
		prefixes := cfg.RequiredObject("prefixes")
		if err := cfg.Validate(); err != nil {
			return fmt.Errorf("configuration error in root object's keys: %v", err)
		}
		for prefix, vei := range prefixes {
			pmap, ok := vei.(map[string]interface{})
			if !ok {
				log.Printf("prefix %q value is a %T, not an object", prefix, vei)
				continue
			}
			pconf := jsonconfig.Obj(pmap)
			handlerType := pconf.RequiredString("handler")
			handlerArgs := pconf.OptionalObject("handlerArgs")
			// no pconf.Validate, as this is a recover tool
			if handlerType != "storage-diskpacked" {
				continue
			}
			if handlerArgs == nil {
				log.Printf("no handlerArgs for %q", prefix)
				continue
			}
			aconf := jsonconfig.Obj(handlerArgs)
			path = aconf.RequiredString("path")
			// no aconv.Validate, as this is a recover tool
			if path != "" {
				break
			}
		}

	case len(args) == 1:
		path = args[0]
	default:
		return errors.New("More than 1 argument not allowed")
	}
	if path == "" {
		return errors.New("no path is given/found")
	}

	return diskpacked.Reindex(path, c.overwrite)
}
示例#6
0
// serverKeyId returns the public gpg key id ("identity" field)
// from the user's server config , if any.
// It returns the empty string otherwise.
func serverKeyId() string {
	serverConfigFile := osutil.UserServerConfigPath()
	if _, err := wkfs.Stat(serverConfigFile); err != nil {
		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
}
示例#7
0
func (c *dumpconfigCmd) RunCommand(args []string) error {
	var file string
	switch {
	case len(args) == 0:
		file = osutil.UserServerConfigPath()
	case len(args) == 1:
		file = args[0]
	default:
		return errors.New("More than 1 argument not allowed")
	}
	cfg, err := serverconfig.Load(file)
	if err != nil {
		return err
	}
	ll, err := json.MarshalIndent(cfg.Obj, "", "  ")
	if err != nil {
		return err
	}
	_, err = os.Stdout.Write(ll)
	return err
}
示例#8
0
// loadConfig returns the server's parsed config file, locating it using the provided arg.
//
// The arg may be of the form:
// - empty, to mean automatic (will write a default high-level config if
//   no cloud config is available)
// - a filepath absolute or relative to the user's configuration directory,
// - a URL
func loadConfig(arg string) (conf *serverinit.Config, isNewConfig bool, err error) {
	if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
		contents, err := slurpURL(arg, 256<<10)
		if err != nil {
			return nil, false, err
		}
		conf, err = serverinit.Load(contents)
		return conf, false, err
	}
	var absPath string
	switch {
	case arg == "":
		absPath = osutil.UserServerConfigPath()
		_, err = wkfs.Stat(absPath)
		if err != nil {
			if !os.IsNotExist(err) {
				return
			}
			conf, err = serverinit.DefaultEnvConfig()
			if err != nil || conf != nil {
				return
			}
			err = wkfs.MkdirAll(osutil.CamliConfigDir(), 0700)
			if err != nil {
				return
			}
			log.Printf("Generating template config file %s", absPath)
			if err = serverinit.WriteDefaultConfigFile(absPath, sqlite.CompiledIn()); err == nil {
				isNewConfig = true
			}
		}
	case filepath.IsAbs(arg):
		absPath = arg
	default:
		absPath = filepath.Join(osutil.CamliConfigDir(), arg)
	}
	conf, err = serverinit.LoadFile(absPath)
	return
}
示例#9
0
// findConfigFile returns the absolute path of the user's
// config file.
// The provided file may be absolute or relative
// to the user's configuration directory.
// If file is empty, a default high-level config is written
// for the user.
func findConfigFile(file string) (absPath string, err error) {
	switch {
	case file == "":
		absPath = osutil.UserServerConfigPath()
		_, err = os.Stat(absPath)
		if os.IsNotExist(err) {
			err = os.MkdirAll(osutil.CamliConfigDir(), 0700)
			if err != nil {
				return
			}
			log.Printf("Generating template config file %s", absPath)
			err = newDefaultConfigFile(absPath)
		}
		return
	case filepath.IsAbs(file):
		absPath = file
	default:
		absPath = filepath.Join(osutil.CamliConfigDir(), file)
	}
	_, err = os.Stat(absPath)
	return
}
示例#10
0
func sendWizard(rw http.ResponseWriter, req *http.Request, hasChanged bool) {
	config, err := jsonconfig.ReadFile(osutil.UserServerConfigPath())
	if err != nil {
		httputil.ServerError(rw, req, err)
		return
	}

	err = flattenPublish(config)
	if err != nil {
		httputil.ServerError(rw, req, err)
		return
	}

	funcMap := template.FuncMap{
		"printWizard":    printWizard,
		"inputIsGallery": func(inputName string) bool { return inputName == "gallery" },
	}

	body := `<form id="WizardForm" action="setup" method="post" enctype="multipart/form-data">`
	body += `{{range $k,$v := .}}{{printf "%v" $k}} <input type="text" size="30" name ="{{printf "%v" $k}}" value="{{printWizard $v}}" {{if inputIsGallery $k}}placeholder="/pics/,sha1-xxxx,pics.css"{{end}}><br />{{end}}`
	body += `<input type="submit" form="WizardForm" value="Save"></form>`

	if hasChanged {
		body += `<p> Configuration succesfully rewritten </p>`
	}

	tmpl, err := template.New("wizard").Funcs(funcMap).Parse(topWizard + body + bottomWizard)
	if err != nil {
		httputil.ServerError(rw, req, err)
		return
	}
	err = tmpl.Execute(rw, config)
	if err != nil {
		httputil.ServerError(rw, req, err)
		return
	}
}
示例#11
0
// findConfigFile returns the absolute path of the user's
// config file.
// The provided file may be absolute or relative
// to the user's configuration directory.
// If file is empty, a default high-level config is written
// for the user.
func findConfigFile(file string) (absPath string, isNewConfig bool, err error) {
	switch {
	case file == "":
		absPath = osutil.UserServerConfigPath()
		_, err = os.Stat(absPath)
		if os.IsNotExist(err) {
			err = os.MkdirAll(osutil.CamliConfigDir(), 0700)
			if err != nil {
				return
			}
			log.Printf("Generating template config file %s", absPath)
			if err = serverinit.WriteDefaultConfigFile(absPath, sqlite.CompiledIn()); err == nil {
				isNewConfig = true
			}
		}
		return
	case filepath.IsAbs(file):
		absPath = file
	default:
		absPath = filepath.Join(osutil.CamliConfigDir(), file)
	}
	_, err = os.Stat(absPath)
	return
}
示例#12
0
func handleSetupChange(rw http.ResponseWriter, req *http.Request) {
	hilevelConf, err := jsonconfig.ReadFile(osutil.UserServerConfigPath())
	if err != nil {
		httputil.ServerError(rw, req, err)
		return
	}

	hasChanged := false
	var el interface{}
	publish := jsonconfig.Obj{}
	for k, v := range req.Form {
		if _, ok := hilevelConf[k]; !ok {
			if k != "gallery" && k != "blog" {
				continue
			}
		}

		switch k {
		case "https":
			b, err := strconv.ParseBool(v[0])
			if err != nil {
				httputil.ServerError(rw, req, fmt.Errorf("https field expects a boolean value"))
			}
			el = b
		case "replicateTo":
			// TODO(mpl): figure out why it is always seen as different from the conf
			el = []interface{}{}
			if len(v[0]) > 0 {
				els := []string{}
				vals := strings.Split(v[0], ",")
				els = append(els, vals...)
				el = els
			}
		// TODO(mpl): "handler,rootPermanode[,style]" for each published entity for now.
		// we will need something more readable later probably
		case "gallery", "blog":
			if len(v[0]) > 0 {
				pub := strings.Split(v[0], ",")
				if len(pub) < 2 || len(pub) > 3 {
					// no need to fail loudly for now as we'll probably change this format
					continue
				}
				handler := jsonconfig.Obj{}
				handler["template"] = k
				handler["rootPermanode"] = pub[1]
				if len(pub) > 2 {
					handler["style"] = pub[2]
				}
				publish[pub[0]] = handler
			}
			continue
		default:
			el = v[0]
		}
		if reflect.DeepEqual(hilevelConf[k], el) {
			continue
		}
		hasChanged = true
		hilevelConf[k] = el
	}
	// "publish" wasn't checked yet
	if !reflect.DeepEqual(hilevelConf["publish"], publish) {
		hilevelConf["publish"] = publish
		hasChanged = true
	}

	if hasChanged {
		err = rewriteConfig(&hilevelConf, osutil.UserServerConfigPath())
		if err != nil {
			httputil.ServerError(rw, req, err)
			return
		}
	}
	sendWizard(rw, req, hasChanged)
	return
}
示例#13
0
// lazy config parsing when there's a known client already.
// The client c may be nil.
func (c *Client) parseConfig() {
	if android.OnAndroid() {
		panic("parseConfig should never have been called on Android")
	}
	if configDisabled {
		panic("parseConfig should never have been called with CAMLI_DISABLE_CLIENT_CONFIG_FILE set")
	}
	configPath := osutil.UserClientConfigPath()
	if _, err := wkfs.Stat(configPath); os.IsNotExist(err) {
		if c != nil && c.isSharePrefix {
			return
		}
		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)
	}
	// 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.
	conf, err := osutil.NewJSONConfigParser().ReadFile(configPath)
	if err != nil {
		log.Fatal(err.Error())
	}
	cfg := jsonconfig.Obj(conf)

	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", ""),
		IdentitySecretRing: cfg.OptionalString("identitySecretRing", ""),
		IgnoredFiles:       cfg.OptionalList("ignoredFiles"),
	}
	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.
		if isURLOrHostPort(alias) {
			log.Fatalf("Server alias %q looks like a hostname; \".\" or \";\" are not allowed.", alias)
		}
		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
	if err := cfg.Validate(); err != nil {
		printConfigChangeHelp(cfg)
		log.Fatalf("Error in config file: %v", err)
	}
}
示例#14
0
func parseConfig() {
	if android.OnAndroid() {
		return
	}
	configPath := osutil.UserClientConfigPath()
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		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)
	}

	conf, err := jsonconfig.ReadFile(configPath)
	if err != nil {
		log.Fatal(err.Error())
	}
	cfg := jsonconfig.Obj(conf)
	config = &clientConfig{
		auth:               cfg.OptionalString("auth", ""),
		server:             cfg.OptionalString("server", ""),
		identity:           cfg.OptionalString("identity", ""),
		identitySecretRing: cfg.OptionalString("identitySecretRing", osutil.IdentitySecretRing()),
		trustedCerts:       cfg.OptionalList("trustedCerts"),
		ignoredFiles:       cfg.OptionalList("ignoredFiles"),
	}
	if err := cfg.Validate(); err != nil {
		printConfigChangeHelp(cfg)
		log.Fatalf("Error in config file: %v", err)
	}
}
示例#15
0
func parseConfig() {
	if android.OnAndroid() {
		return
	}
	configPath := osutil.UserClientConfigPath()
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		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)
	}

	var err error
	if config, err = jsonconfig.ReadFile(configPath); err != nil {
		log.Fatal(err.Error())
		return
	}
}
示例#16
0
func (c *reindexdpCmd) RunCommand(args []string) error {
	var path string
	var indexConf jsonconfig.Obj

	switch len(args) {
	case 0:
	case 1:
		path = args[0]
	default:
		return errors.New("More than 1 argument not allowed")
	}
	cfg, err := serverinit.LoadFile(osutil.UserServerConfigPath())
	if err != nil {
		return err
	}
	prefixes, ok := cfg.Obj["prefixes"].(map[string]interface{})
	if !ok {
		return fmt.Errorf("No 'prefixes' object in low-level (or converted) config file %s", osutil.UserServerConfigPath())
	}
	paths, confs := []string{}, []jsonconfig.Obj{}
	for prefix, vei := range prefixes {
		pmap, ok := vei.(map[string]interface{})
		if !ok {
			log.Printf("prefix %q value is a %T, not an object", prefix, vei)
			continue
		}
		pconf := jsonconfig.Obj(pmap)
		handlerType := pconf.RequiredString("handler")
		handlerArgs := pconf.OptionalObject("handlerArgs")
		// no pconf.Validate, as this is a recover tool
		if handlerType != "storage-diskpacked" {
			continue
		}
		log.Printf("handlerArgs of %q: %v", prefix, handlerArgs)
		if handlerArgs == nil {
			log.Printf("no handlerArgs for %q", prefix)
			continue
		}
		aconf := jsonconfig.Obj(handlerArgs)
		apath := aconf.RequiredString("path")
		// no aconv.Validate, as this is a recover tool
		if apath == "" {
			log.Printf("path is missing for %q", prefix)
			continue
		}
		if path != "" && path != apath {
			continue
		}
		paths = append(paths, apath)
		confs = append(confs, aconf)
	}
	if len(paths) == 0 {
		return fmt.Errorf("Server config file %s doesn't specify a disk-packed storage handler.",
			osutil.UserServerConfigPath())
	}
	if len(paths) > 1 {
		return fmt.Errorf("Ambiguity. Server config file %s d specify more than 1 disk-packed storage handler. Please specify one of: %v", osutil.UserServerConfigPath(), paths)
	}
	path = paths[0]
	if path == "" {
		return errors.New("no path is given/found")
	}
	// If no index is specified, the default will be used (as on the regular path).
	if mi := confs[0]["metaIndex"]; mi != nil {
		if mi, ok := mi.(map[string]interface{}); ok {
			indexConf = jsonconfig.Obj(mi)
		}
	}
	log.Printf("indexConf: %v", indexConf)

	return diskpacked.Reindex(path, c.overwrite, indexConf)
}