-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.go
233 lines (219 loc) · 5.75 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/signal"
"path/filepath"
"syscall"
"github.com/FarmRadioHangar/fessboxconfig/device"
"github.com/FarmRadioHangar/fessboxconfig/parser"
"github.com/gernest/hot"
"github.com/gorilla/mux"
)
//Config holds configuration values for this application.
type Config struct {
Port int64 `json:"port"`
Host string `json:"host"`
StaticDir string `json:"static_dir"`
TemplatesDir string `json:"templates_dir"`
AsteriskConfig string `json:"asterisk_config_dir"`
Autodetect bool `json:"autodetect"`
}
func defaultConfig() *Config {
return &Config{
Port: 8080,
Host: "",
StaticDir: "static",
TemplatesDir: "templates",
AsteriskConfig: "/etc/asterisk",
Autodetect: true,
}
}
func main() {
c := flag.String("c", "etc/fconf.json", "path to the configuration file")
dev := flag.Bool("dev", false, "set true if running in dev mode")
flag.Parse()
b, err := ioutil.ReadFile(*c)
if err != nil {
log.Fatal(err)
}
cfg := &Config{}
err = json.Unmarshal(b, cfg)
if err != nil {
log.Fatal(err)
}
var manager *device.Manager
if cfg.Autodetect {
manager = device.New()
manager.Init()
}
if *dev {
cfg.AsteriskConfig = "sample"
tmp, err := ioutil.TempDir("", "fconf")
if err != nil {
log.Fatal(err)
}
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)
go func(dir string) {
END:
for {
select {
case <-c:
log.Println("removing ", dir)
_ = os.RemoveAll(dir)
if cfg.Autodetect {
manager.Close()
}
os.Exit(0)
break END
}
}
}(tmp)
cfg.AsteriskConfig = tmp
err = copyFiles(tmp, "sample")
if err != nil {
log.Fatal(err)
}
}
s := newServer(cfg)
s.HandleFunc("/serial/list", manager.List).Methods("GET")
log.Printf(" starting server on localhost:%d\n", cfg.Port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", cfg.Port), s))
}
//copyFiles copies files from src to dst, directories are ignored
func copyFiles(dst, src string) error {
files, err := ioutil.ReadDir(src)
if err != nil {
return err
}
for _, file := range files {
if file.IsDir() {
continue
}
fname := filepath.Join(src, file.Name())
f, err := ioutil.ReadFile(fname)
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(dst, file.Name()), f, 0600)
if err != nil {
return err
}
}
return nil
}
//newServer returns a http.Handler with all the routes for configuring supported
//devices registered.
func newServer(c *Config) *mux.Router {
s := mux.NewRouter()
w := newWeb(c)
s.HandleFunc("/config/{filename}", w.Dongle).Methods("GET")
s.HandleFunc("/config/{filename}", w.UpdateDongle).Methods("POST")
//s.PathPrefix("/static/").
//Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(c.StaticDir))))
s.HandleFunc("/", w.Home)
return s
}
// web is the application struct, it defines all the handlers for this
// application as its methods.
//
// It is safe to use this in multiple goroutines
//
// This contains a a loaded hot template, so when run in development mode the
// templates will automatially be reloaded without the need to re run the
// application process. The auto reloading of templates is disabled in
// production.
type web struct {
cfg *Config
tpl *hot.Template
}
//newWeb intialises and returns a new instance of *web, the templates are loaded
//and if dev mode is set to true then auto reload is enabled.
func newWeb(cfg *Config) *web {
return &web{cfg: cfg}
}
//Home serves the home page
func (ww *web) Home(w http.ResponseWriter, r *http.Request) {
}
type errMSG struct {
Message string `json:"error"`
}
// Dongle implements http.HandleFunc for serving the dongle configuration values
// as a json object
//
// This only returns the current values of the dongle configuration file, so it
// is good for GET requests only.
func (ww *web) Dongle(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
file := vars["filename"] + ".conf"
fName := filepath.Join(ww.cfg.AsteriskConfig, file)
enc := json.NewEncoder(w)
w.Header().Set("Content-Type", "application/json")
f, err := os.Open(fName)
if err != nil {
log.Println(err)
_ = enc.Encode(&errMSG{"trouble opening dongle configuration"})
return
}
defer func() { _ = f.Close() }()
p, err := parser.NewParser(f)
if err != nil {
log.Println(err)
_ = enc.Encode(&errMSG{"trouble scanning dongle configuration"})
return
}
ast, err := p.Parse()
if err != nil {
log.Println(err)
_ = enc.Encode(&errMSG{"trouble parsing dongle configuration"})
return
}
err = ast.ToJSON(w)
if err != nil {
log.Println(err)
}
}
//UpdateDongle updates the dongle documentation file, via a json object. This
//doesnot do verification of the object sent with the request.
//
// The received json is loaded into ast and writen to the dongle configuration
// file directly.
//
// TODO(gernest) do some kind of verification?
func (ww *web) UpdateDongle(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
ast := &parser.Ast{}
src := &bytes.Buffer{}
enc := json.NewEncoder(w)
_, err := io.Copy(src, r.Body)
if err != nil {
_ = enc.Encode(&errMSG{Message: "trouble reading request body"})
return
}
err = ast.LoadJSON(src.Bytes())
if err != nil {
_ = enc.Encode(&errMSG{Message: "trouble loading request body"})
return
}
vars := mux.Vars(r)
file := vars["filename"] + ".conf"
fName := filepath.Join(ww.cfg.AsteriskConfig, file)
info, err := os.Stat(fName)
if err != nil {
log.Println(err)
_ = enc.Encode(&errMSG{"trouble opening dongle configuration"})
return
}
dst := &bytes.Buffer{}
parser.PrintAst(dst, ast)
_ = ioutil.WriteFile(fName, dst.Bytes(), info.Mode())
_, _ = io.Copy(w, src)
}