Initial main commit.
[src/xds/xds-server.git] / lib / xdsconfig / fileconfig.go
1 package xdsconfig
2
3 import (
4         "encoding/json"
5         "os"
6         "os/user"
7         "path"
8         "path/filepath"
9         "regexp"
10         "strings"
11 )
12
13 type SyncThingConf struct {
14         Home       string `json:"home"`
15         GuiAddress string `json:"gui-address"`
16         GuiAPIKey  string `json:"gui-apikey"`
17 }
18
19 type FileConfig struct {
20         WebAppDir    string        `json:"webAppDir"`
21         ShareRootDir string        `json:"shareRootDir"`
22         HTTPPort     string        `json:"httpPort"`
23         SThgConf     SyncThingConf `json:"syncthing"`
24 }
25
26 // getConfigFromFile reads configuration from a config file.
27 // Order to determine which config file is used:
28 //  1/ from command line option: "--config myConfig.json"
29 //  2/ $HOME/.xds/config.json file
30 //  3/ <xds-server executable dir>/config.json file
31
32 func updateConfigFromFile(c *Config, confFile string) error {
33
34         searchIn := make([]string, 0, 3)
35         if confFile != "" {
36                 searchIn = append(searchIn, confFile)
37         }
38         if usr, err := user.Current(); err == nil {
39                 searchIn = append(searchIn, path.Join(usr.HomeDir, ".xds", "config.json"))
40         }
41         cwd, err := os.Getwd()
42         if err == nil {
43                 searchIn = append(searchIn, path.Join(cwd, "config.json"))
44         }
45         exePath, err := filepath.Abs(filepath.Dir(os.Args[0]))
46         if err == nil {
47                 searchIn = append(searchIn, path.Join(exePath, "config.json"))
48         }
49
50         var cFile *string
51         for _, p := range searchIn {
52                 if _, err := os.Stat(p); err == nil {
53                         cFile = &p
54                         break
55                 }
56         }
57         if cFile == nil {
58                 // No config file found
59                 return nil
60         }
61
62         // TODO move on viper package to support comments in JSON and also
63         // bind with flags (command line options)
64         // see https://github.com/spf13/viper#working-with-flags
65
66         fd, _ := os.Open(*cFile)
67         defer fd.Close()
68         fCfg := FileConfig{}
69         if err := json.NewDecoder(fd).Decode(&fCfg); err != nil {
70                 return err
71         }
72         c.fileConf = fCfg
73
74         // Support environment variables (IOW ${MY_ENV_VAR} syntax) in config.json
75         // TODO: better to use reflect package to iterate on fields and be more generic
76         fCfg.WebAppDir = path.Clean(resolveEnvVar(fCfg.WebAppDir))
77         fCfg.ShareRootDir = path.Clean(resolveEnvVar(fCfg.ShareRootDir))
78         fCfg.SThgConf.Home = path.Clean(resolveEnvVar(fCfg.SThgConf.Home))
79
80         // Config file settings overwrite default config
81
82         if fCfg.WebAppDir != "" {
83                 c.WebAppDir = strings.Trim(fCfg.WebAppDir, " ")
84         }
85         // Is it a full path ?
86         if !strings.HasPrefix(c.WebAppDir, "/") && exePath != "" {
87                 // Check first from current directory
88                 for _, rootD := range []string{cwd, exePath} {
89                         ff := path.Join(rootD, c.WebAppDir, "index.html")
90                         if exists(ff) {
91                                 c.WebAppDir = path.Join(rootD, c.WebAppDir)
92                                 break
93                         }
94                 }
95         }
96
97         if fCfg.ShareRootDir != "" {
98                 c.ShareRootDir = fCfg.ShareRootDir
99         }
100
101         if fCfg.HTTPPort != "" {
102                 c.HTTPPort = fCfg.HTTPPort
103         }
104
105         return nil
106 }
107
108 // resolveEnvVar Resolved environment variable regarding the syntax ${MYVAR}
109 func resolveEnvVar(s string) string {
110         re := regexp.MustCompile("\\${(.*)}")
111         vars := re.FindAllStringSubmatch(s, -1)
112         res := s
113         for _, v := range vars {
114                 val := os.Getenv(v[1])
115                 if val != "" {
116                         rer := regexp.MustCompile("\\${" + v[1] + "}")
117                         res = rer.ReplaceAllString(res, val)
118                 }
119         }
120         return res
121 }
122
123 // exists returns whether the given file or directory exists or not
124 func exists(path string) bool {
125         _, err := os.Stat(path)
126         if err == nil {
127                 return true
128         }
129         if os.IsNotExist(err) {
130                 return false
131         }
132         return true
133 }