source: code/trunk/partage.go@ 19

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

Improve error handling for writefile()

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