source: code/trunk/partage.go@ 24

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

Hide true error messages

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