source: code/trunk/partage.go@ 26

Last change on this file since 26 was 26, checked in by dev, 4 years ago

Accept flags as INI file config

File size: 5.4 KB
Line 
1package main
2
3import (
4 "fmt"
5 "flag"
6 "io"
7 "io/ioutil"
8 "net/http"
9 "os"
10 "time"
11 "path"
12 "path/filepath"
13 "html/template"
14 "encoding/json"
15
16 "github.com/dustin/go-humanize"
17 "github.com/vharitonsky/iniflags"
18)
19
20type templatedata struct {
21 Links []string
22 Size string
23 Maxsize string
24}
25
26type metadata struct {
27 Filename string
28 Size int64
29 Expiry int64
30}
31
32var conf struct {
33 bind string
34 baseuri string
35 filepath string
36 metapath string
37 rootdir string
38 templatedir string
39 filectx string
40 metactx string
41 maxsize int64
42 expiry int64
43}
44
45func writefile(f *os.File, s io.ReadCloser, contentlength int64) error {
46 buffer := make([]byte, 4096)
47 eof := false
48 sz := int64(0)
49
50 defer f.Sync()
51
52 for !eof {
53 n, err := s.Read(buffer)
54 if err != nil && err != io.EOF {
55 return err
56 } else if err == io.EOF {
57 eof = true
58 }
59
60 /* ensure we don't write more than expected */
61 r := int64(n)
62 if sz+r > contentlength {
63 r = contentlength - sz
64 eof = true
65 }
66
67 _, err = f.Write(buffer[:r])
68 if err != nil {
69 return err
70 }
71 sz += r
72 }
73
74 return nil
75}
76
77func writemeta(filename string, expiry int64) error {
78
79 f, _ := os.Open(filename)
80 stat, _ := f.Stat()
81 size := stat.Size()
82 f.Close()
83
84 meta := metadata{
85 Filename: filepath.Base(filename),
86 Size: size,
87 Expiry: time.Now().Unix() + expiry,
88 }
89
90 f, err := os.Create(conf.metapath + "/" + meta.Filename + ".json")
91 if err != nil {
92 return err
93 }
94 defer f.Close()
95
96 j, err := json.Marshal(meta)
97 if err != nil {
98 return err
99 }
100
101 _, err = f.Write(j)
102
103 return err
104}
105
106func servetemplate(w http.ResponseWriter, f string, d templatedata) {
107 t, err := template.ParseFiles(conf.templatedir + "/" + f)
108 if err != nil {
109 http.Error(w, "Internal error", http.StatusInternalServerError)
110 return
111 }
112
113 err = t.Execute(w, d)
114 if err != nil {
115 fmt.Println(err)
116 }
117}
118
119func uploaderPut(w http.ResponseWriter, r *http.Request) {
120 /* limit upload size */
121 if r.ContentLength > conf.maxsize {
122 http.Error(w, "File is too big", http.StatusRequestEntityTooLarge)
123 }
124
125 tmp, _ := ioutil.TempFile(conf.filepath, "*"+path.Ext(r.URL.Path))
126 f, err := os.Create(tmp.Name())
127 if err != nil {
128 fmt.Println(err)
129 return
130 }
131 defer f.Close()
132
133 if err = writefile(f, r.Body, r.ContentLength); err != nil {
134 http.Error(w, "Internal error", http.StatusInternalServerError)
135 defer os.Remove(tmp.Name())
136 return
137 }
138 writemeta(tmp.Name(), conf.expiry)
139
140 resp := conf.baseuri + conf.filectx + filepath.Base(tmp.Name())
141 w.Write([]byte(resp))
142}
143
144func uploaderPost(w http.ResponseWriter, r *http.Request) {
145 /* read 32Mb at a time */
146 r.ParseMultipartForm(32 << 20)
147
148 links := []string{}
149 for _, h := range r.MultipartForm.File["uck"] {
150 if h.Size > conf.maxsize {
151 http.Error(w, "File is too big", http.StatusRequestEntityTooLarge)
152 return
153 }
154
155 post, err := h.Open()
156 if err != nil {
157 http.Error(w, "Internal error", http.StatusInternalServerError)
158 return
159 }
160 defer post.Close()
161
162 tmp, _ := ioutil.TempFile(conf.filepath, "*"+path.Ext(h.Filename))
163 f, err := os.Create(tmp.Name())
164 if err != nil {
165 http.Error(w, "Internal error", http.StatusInternalServerError)
166 return
167 }
168 defer f.Close()
169
170 if err = writefile(f, post, h.Size); err != nil {
171 http.Error(w, "Internal error", http.StatusInternalServerError)
172 defer os.Remove(tmp.Name())
173 return
174 }
175
176 writemeta(tmp.Name(), conf.expiry)
177
178
179 link := conf.baseuri + conf.filectx + filepath.Base(tmp.Name())
180 links = append(links, link)
181 }
182
183 if (r.PostFormValue("output") == "html") {
184 data := templatedata{ Links: links }
185 servetemplate(w, "/upload.html", data)
186 return
187 } else {
188 for _, link := range links {
189 w.Write([]byte(link + "\r\n"))
190 }
191 }
192}
193
194func uploaderGet(w http.ResponseWriter, r *http.Request) {
195 // r.URL.Path is sanitized regarding "." and ".."
196 filename := r.URL.Path
197 if r.URL.Path == "/" || r.URL.Path == "/index.html" {
198 data := templatedata{ Maxsize: humanize.IBytes(uint64(conf.maxsize))}
199 servetemplate(w, "/index.html", data)
200 return
201 }
202
203 http.ServeFile(w, r, conf.rootdir + filename)
204}
205
206func uploader(w http.ResponseWriter, r *http.Request) {
207 switch r.Method {
208 case "POST":
209 uploaderPost(w, r)
210 case "PUT":
211 uploaderPut(w, r)
212 case "GET":
213 uploaderGet(w, r)
214 }
215}
216
217func main() {
218 flag.StringVar(&conf.bind, "bind", "0.0.0.0:8080", "Address to bind to (default: 0.0.0.0:8080)")
219 flag.StringVar(&conf.baseuri, "baseuri", "http://127.0.0.1:8080", "Base URI to use for links (default: http://127.0.0.1:8080)")
220 flag.StringVar(&conf.filepath, "filepath", "./files", "Path to save files to (default: ./files)")
221 flag.StringVar(&conf.metapath, "metapath", "./meta", "Path to save metadata to (default: ./meta)")
222 flag.StringVar(&conf.filectx, "filectx", "/f/", "Context to serve files from (default: /f/)")
223 flag.StringVar(&conf.metactx, "metactx", "/m/", "Context to serve metadata from (default: /m/)")
224 flag.StringVar(&conf.rootdir, "rootdir", "./static", "Root directory (default: ./static)")
225 flag.StringVar(&conf.templatedir, "templatedir", "./templates", "Templates directory (default: ./templates)")
226 flag.Int64Var(&conf.maxsize, "maxsize", 30064771072, "Maximum file size (default: 28Gib)")
227 flag.Int64Var(&conf.expiry, "expiry", 86400, "Link expiration time (default: 24h)")
228
229 iniflags.Parse()
230
231 http.HandleFunc("/", uploader)
232 http.Handle(conf.filectx, http.StripPrefix(conf.filectx, http.FileServer(http.Dir(conf.filepath))))
233 http.ListenAndServe(conf.bind, nil)
234}
Note: See TracBrowser for help on using the repository browser.