source: code/trunk/partage.go@ 20

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

Write metadata about the file along with it

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