You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

945 lines
24 KiB

  1. /*
  2. The MIT License (MIT)
  3. Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. THE SOFTWARE.
  19. */
  20. package server
  21. import (
  22. "archive/tar"
  23. "archive/zip"
  24. "bytes"
  25. "compress/gzip"
  26. "encoding/base64"
  27. "errors"
  28. "fmt"
  29. "html"
  30. htmlTemplate "html/template"
  31. "io"
  32. "io/ioutil"
  33. "log"
  34. "math/rand"
  35. "mime"
  36. "net/http"
  37. "net/url"
  38. "os"
  39. "path"
  40. "path/filepath"
  41. "strconv"
  42. "strings"
  43. "sync"
  44. textTemplate "text/template"
  45. "time"
  46. "github.com/dutchcoders/go-clamd"
  47. "github.com/dutchcoders/go-virustotal"
  48. "github.com/dutchcoders/transfer.sh/server/storage"
  49. "github.com/dutchcoders/transfer.sh/server/utils"
  50. "github.com/gorilla/mux"
  51. "github.com/microcosm-cc/bluemonday"
  52. "github.com/russross/blackfriday/v2"
  53. "github.com/skip2/go-qrcode"
  54. )
  55. var (
  56. htmlTemplates = initHTMLTemplates()
  57. textTemplates = initTextTemplates()
  58. )
  59. func initTextTemplates() *textTemplate.Template {
  60. templateMap := textTemplate.FuncMap{"format": utils.FormatNumber}
  61. // Templates with functions available to them
  62. var templates = textTemplate.New("").Funcs(templateMap)
  63. return templates
  64. }
  65. func initHTMLTemplates() *htmlTemplate.Template {
  66. templateMap := htmlTemplate.FuncMap{"format": utils.FormatNumber}
  67. // Templates with functions available to them
  68. var templates = htmlTemplate.New("").Funcs(templateMap)
  69. return templates
  70. }
  71. // Create a log handler for every request it receives.
  72. func (s *Server) LoveHandler(h http.Handler) http.HandlerFunc {
  73. return func(w http.ResponseWriter, r *http.Request) {
  74. w.Header().Set("x-made-with", "<3 by DutchCoders")
  75. w.Header().Set("x-served-by", "Proudly served by DutchCoders")
  76. w.Header().Set("Server", "Transfer.sh HTTP Server 1.0")
  77. h.ServeHTTP(w, r)
  78. }
  79. }
  80. func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc {
  81. return func(w http.ResponseWriter, r *http.Request) {
  82. if !s.forceHTTPs {
  83. // we don't want to enforce https
  84. } else if r.URL.Path == "/health.html" {
  85. // health check url won't redirect
  86. } else if strings.HasSuffix(utils.IpAddrFromRemoteAddr(r.Host), ".onion") {
  87. // .onion addresses cannot get a valid certificate, so don't redirect
  88. } else if r.Header.Get("X-Forwarded-Proto") == "https" {
  89. } else if r.URL.Scheme == "https" {
  90. } else {
  91. u := utils.GetURL(r)
  92. u.Scheme = "https"
  93. http.Redirect(w, r, u.String(), http.StatusPermanentRedirect)
  94. return
  95. }
  96. h.ServeHTTP(w, r)
  97. }
  98. }
  99. func (s *Server) BasicAuthHandler(h http.Handler) http.HandlerFunc {
  100. return func(w http.ResponseWriter, r *http.Request) {
  101. if s.AuthUser == "" || s.AuthPass == "" {
  102. h.ServeHTTP(w, r)
  103. return
  104. }
  105. w.Header().Set("WWW-Authenticate", "Basic realm=\"Restricted\"")
  106. username, password, authOK := r.BasicAuth()
  107. if authOK == false {
  108. http.Error(w, "Not authorized", 401)
  109. return
  110. }
  111. if username != s.AuthUser || password != s.AuthPass {
  112. http.Error(w, "Not authorized", 401)
  113. return
  114. }
  115. h.ServeHTTP(w, r)
  116. }
  117. }
  118. func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) {
  119. _, _ = w.Write([]byte("Approaching Neutral Zone, all systems normal and functioning."))
  120. }
  121. // The preview handler will show a preview of the content for browsers (accept type text/html), and referer is not transfer.sh
  122. func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) {
  123. vars := mux.Vars(r)
  124. token := vars["token"]
  125. filename := vars["filename"]
  126. metadata, err := s.checkMetadata(token, filename, false)
  127. if err != nil {
  128. log.Printf("Error metadata: %s", err.Error())
  129. http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
  130. return
  131. }
  132. contentType := metadata.ContentType
  133. var templatePath string
  134. var content htmlTemplate.HTML
  135. switch {
  136. case strings.HasPrefix(contentType, "image/"):
  137. templatePath = "download.image.html"
  138. case strings.HasPrefix(contentType, "video/"):
  139. templatePath = "download.video.html"
  140. case strings.HasPrefix(contentType, "audio/"):
  141. templatePath = "download.audio.html"
  142. case strings.HasPrefix(contentType, "text/"):
  143. templatePath = "download.markdown.html"
  144. var reader io.ReadCloser
  145. if reader, _, err = s.storage.Get(token, filename); err != nil {
  146. http.Error(w, err.Error(), http.StatusInternalServerError)
  147. return
  148. }
  149. var data []byte
  150. data = make([]byte, _5M)
  151. if _, err = reader.Read(data); err != io.EOF && err != nil {
  152. http.Error(w, err.Error(), http.StatusInternalServerError)
  153. return
  154. }
  155. if strings.HasPrefix(contentType, "text/x-markdown") || strings.HasPrefix(contentType, "text/markdown") {
  156. unsafe := blackfriday.Run(data)
  157. output := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
  158. content = htmlTemplate.HTML(output)
  159. } else if strings.HasPrefix(contentType, "text/plain") {
  160. content = htmlTemplate.HTML(fmt.Sprintf("<pre>%s</pre>", html.EscapeString(string(data))))
  161. } else {
  162. templatePath = "download.sandbox.html"
  163. }
  164. default:
  165. templatePath = "download.html"
  166. }
  167. relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename))
  168. resolvedURL := utils.ResolveURL(r, relativeURL)
  169. relativeURLGet, _ := url.Parse(path.Join(s.proxyPath, "get", token, filename))
  170. resolvedURLGet := utils.ResolveURL(r, relativeURLGet)
  171. var png []byte
  172. png, err = qrcode.Encode(resolvedURL, qrcode.High, 150)
  173. if err != nil {
  174. http.Error(w, err.Error(), http.StatusInternalServerError)
  175. return
  176. }
  177. qrCode := base64.StdEncoding.EncodeToString(png)
  178. hostname := utils.GetURL(r).Host
  179. webAddress := utils.ResolveWebAddress(r, s.proxyPath)
  180. data := struct {
  181. ContentType string
  182. Content htmlTemplate.HTML
  183. Filename string
  184. Url string
  185. UrlGet string
  186. Hostname string
  187. WebAddress string
  188. ContentLength int64
  189. GAKey string
  190. UserVoiceKey string
  191. QRCode string
  192. }{
  193. contentType,
  194. content,
  195. filename,
  196. resolvedURL,
  197. resolvedURLGet,
  198. hostname,
  199. webAddress,
  200. metadata.ContentLength,
  201. s.gaKey,
  202. s.userVoiceKey,
  203. qrCode,
  204. }
  205. if err := htmlTemplates.ExecuteTemplate(w, templatePath, data); err != nil {
  206. http.Error(w, err.Error(), http.StatusInternalServerError)
  207. return
  208. }
  209. }
  210. // this handler will output html or text, depending on the support of the client (Accept header).
  211. func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) {
  212. // vars := mux.Vars(r)
  213. hostname := utils.GetURL(r).Host
  214. webAddress := utils.ResolveWebAddress(r, s.proxyPath)
  215. data := struct {
  216. Hostname string
  217. WebAddress string
  218. GAKey string
  219. UserVoiceKey string
  220. }{
  221. hostname,
  222. webAddress,
  223. s.gaKey,
  224. s.userVoiceKey,
  225. }
  226. if utils.AcceptsHTML(r.Header) {
  227. if err := htmlTemplates.ExecuteTemplate(w, "index.html", data); err != nil {
  228. http.Error(w, err.Error(), http.StatusInternalServerError)
  229. return
  230. }
  231. } else {
  232. if err := textTemplates.ExecuteTemplate(w, "index.txt", data); err != nil {
  233. http.Error(w, err.Error(), http.StatusInternalServerError)
  234. return
  235. }
  236. }
  237. }
  238. func (s *Server) notFoundHandler(w http.ResponseWriter, r *http.Request) {
  239. http.Error(w, http.StatusText(404), 404)
  240. }
  241. func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
  242. if err := r.ParseMultipartForm(_24K); nil != err {
  243. log.Printf("%s", err.Error())
  244. http.Error(w, "Error occurred copying to output stream", 500)
  245. return
  246. }
  247. token := utils.Encode(10000000 + int64(rand.Intn(1000000000)))
  248. w.Header().Set("Content-Type", "text/plain")
  249. for _, fheaders := range r.MultipartForm.File {
  250. for _, fheader := range fheaders {
  251. filename := utils.Sanitize(fheader.Filename)
  252. contentType := fheader.Header.Get("Content-Type")
  253. if contentType == "" {
  254. contentType = mime.TypeByExtension(filepath.Ext(fheader.Filename))
  255. }
  256. var f io.Reader
  257. var err error
  258. if f, err = fheader.Open(); err != nil {
  259. log.Printf("%s", err.Error())
  260. http.Error(w, err.Error(), 500)
  261. return
  262. }
  263. var b bytes.Buffer
  264. n, err := io.CopyN(&b, f, _24K+1)
  265. if err != nil && err != io.EOF {
  266. log.Printf("%s", err.Error())
  267. http.Error(w, err.Error(), 500)
  268. return
  269. }
  270. var file *os.File
  271. var reader io.Reader
  272. if n > _24K {
  273. file, err = ioutil.TempFile(s.tempPath, "transfer-")
  274. if err != nil {
  275. log.Fatal(err)
  276. }
  277. defer utils.CleanTmpFile(file)
  278. n, err = io.Copy(file, io.MultiReader(&b, f))
  279. if err != nil {
  280. log.Printf("%s", err.Error())
  281. http.Error(w, err.Error(), 500)
  282. return
  283. }
  284. reader, err = os.Open(file.Name())
  285. } else {
  286. reader = bytes.NewReader(b.Bytes())
  287. }
  288. metadata := s.metadataForRequest(contentType, n, r)
  289. log.Printf("Uploading %s %s %d %s", token, filename, metadata.ContentLength, metadata.ContentType)
  290. if err = s.storage.Put(token, filename, reader, metadata); err != nil {
  291. log.Printf("Backend storage error: %s", err.Error())
  292. http.Error(w, err.Error(), 500)
  293. return
  294. }
  295. filename = url.PathEscape(filename)
  296. relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename))
  297. deleteURL, _ := url.Parse(path.Join(s.proxyPath, token, filename, metadata.DeletionToken))
  298. w.Header().Set("X-Url-Delete", utils.ResolveURL(r, deleteURL))
  299. _, _ = fmt.Fprintln(w, utils.GetURL(r).ResolveReference(relativeURL).String())
  300. }
  301. }
  302. }
  303. func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
  304. vars := mux.Vars(r)
  305. filename := utils.Sanitize(vars["filename"])
  306. contentLength := r.ContentLength
  307. var reader io.Reader
  308. reader = r.Body
  309. defer r.Body.Close()
  310. if contentLength == -1 {
  311. // queue file to disk, because s3 needs content length
  312. var err error
  313. var f io.Reader
  314. f = reader
  315. var b bytes.Buffer
  316. n, err := io.CopyN(&b, f, _24K+1)
  317. if err != nil && err != io.EOF {
  318. log.Printf("Error putting new file: %s", err.Error())
  319. http.Error(w, err.Error(), 500)
  320. return
  321. }
  322. var file *os.File
  323. if n > _24K {
  324. file, err = ioutil.TempFile(s.tempPath, "transfer-")
  325. if err != nil {
  326. log.Printf("%s", err.Error())
  327. http.Error(w, err.Error(), 500)
  328. return
  329. }
  330. defer utils.CleanTmpFile(file)
  331. n, err = io.Copy(file, io.MultiReader(&b, f))
  332. if err != nil {
  333. log.Printf("%s", err.Error())
  334. http.Error(w, err.Error(), 500)
  335. return
  336. }
  337. reader, err = os.Open(file.Name())
  338. } else {
  339. reader = bytes.NewReader(b.Bytes())
  340. }
  341. contentLength = n
  342. }
  343. if contentLength == 0 {
  344. log.Print("Empty content-length")
  345. http.Error(w, errors.New("could not upload empty file").Error(), 400)
  346. return
  347. }
  348. contentType := r.Header.Get("Content-Type")
  349. if contentType == "" {
  350. contentType = mime.TypeByExtension(filepath.Ext(vars["filename"]))
  351. }
  352. token := utils.Encode(10000000 + int64(rand.Intn(1000000000)))
  353. metadata := s.metadataForRequest(contentType, contentLength, r)
  354. log.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType)
  355. var err error
  356. if err = s.storage.Put(token, filename, reader, metadata); err != nil {
  357. log.Printf("Error putting new file: %s", err.Error())
  358. http.Error(w, errors.New("could not save file").Error(), 500)
  359. return
  360. }
  361. // w.Statuscode = 200
  362. w.Header().Set("Content-Type", "text/plain")
  363. filename = url.PathEscape(filename)
  364. relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename))
  365. deleteURL, _ := url.Parse(path.Join(s.proxyPath, token, filename, metadata.DeletionToken))
  366. w.Header().Set("X-Url-Delete", utils.ResolveURL(r, deleteURL))
  367. _, _ = fmt.Fprint(w, utils.ResolveURL(r, relativeURL))
  368. }
  369. func (s *Server) deleteHandler(w http.ResponseWriter, r *http.Request) {
  370. vars := mux.Vars(r)
  371. token := vars["token"]
  372. filename := vars["filename"]
  373. deletionToken := vars["deletionToken"]
  374. if err := s.checkDeletionToken(deletionToken, token, filename); err != nil {
  375. log.Printf("Error metadata: %s", err.Error())
  376. http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
  377. return
  378. }
  379. err := s.storage.Delete(token, filename)
  380. if s.storage.IsNotExist(err) {
  381. http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
  382. return
  383. } else if err != nil {
  384. log.Printf("%s", err.Error())
  385. http.Error(w, "Could not delete file.", 500)
  386. return
  387. }
  388. }
  389. func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) {
  390. vars := mux.Vars(r)
  391. files := vars["files"]
  392. zipfilename := fmt.Sprintf("transfersh-%d.zip", uint16(time.Now().UnixNano()))
  393. w.Header().Set("Content-Type", "application/zip")
  394. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", zipfilename))
  395. w.Header().Set("Connection", "close")
  396. zw := zip.NewWriter(w)
  397. for _, key := range strings.Split(files, ",") {
  398. key = utils.ResolveKey(key, s.proxyPath)
  399. token := strings.Split(key, "/")[0]
  400. filename := utils.Sanitize(strings.Split(key, "/")[1])
  401. if _, err := s.checkMetadata(token, filename, true); err != nil {
  402. log.Printf("Error metadata: %s", err.Error())
  403. continue
  404. }
  405. reader, _, err := s.storage.Get(token, filename)
  406. if err != nil {
  407. if s.storage.IsNotExist(err) {
  408. http.Error(w, "File not found", 404)
  409. return
  410. } else {
  411. log.Printf("%s", err.Error())
  412. http.Error(w, "Could not retrieve file.", 500)
  413. return
  414. }
  415. }
  416. defer reader.Close()
  417. header := &zip.FileHeader{
  418. Name: strings.Split(key, "/")[1],
  419. Method: zip.Store,
  420. Modified: time.Now(),
  421. }
  422. fw, err := zw.CreateHeader(header)
  423. if err != nil {
  424. log.Printf("%s", err.Error())
  425. http.Error(w, "Internal server error.", 500)
  426. return
  427. }
  428. if _, err = io.Copy(fw, reader); err != nil {
  429. log.Printf("%s", err.Error())
  430. http.Error(w, "Internal server error.", 500)
  431. return
  432. }
  433. }
  434. if err := zw.Close(); err != nil {
  435. log.Printf("%s", err.Error())
  436. http.Error(w, "Internal server error.", 500)
  437. return
  438. }
  439. }
  440. func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) {
  441. vars := mux.Vars(r)
  442. files := vars["files"]
  443. tarfilename := fmt.Sprintf("transfersh-%d.tar.gz", uint16(time.Now().UnixNano()))
  444. w.Header().Set("Content-Type", "application/x-gzip")
  445. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", tarfilename))
  446. w.Header().Set("Connection", "close")
  447. writer := gzip.NewWriter(w)
  448. defer writer.Close()
  449. zw := tar.NewWriter(writer)
  450. defer zw.Close()
  451. for _, key := range strings.Split(files, ",") {
  452. key = utils.ResolveKey(key, s.proxyPath)
  453. token := strings.Split(key, "/")[0]
  454. filename := utils.Sanitize(strings.Split(key, "/")[1])
  455. if _, err := s.checkMetadata(token, filename, true); err != nil {
  456. log.Printf("Error metadata: %s", err.Error())
  457. continue
  458. }
  459. reader, metadata, err := s.storage.Get(token, filename)
  460. if err != nil {
  461. if s.storage.IsNotExist(err) {
  462. http.Error(w, "File not found", 404)
  463. return
  464. } else {
  465. log.Printf("%s", err.Error())
  466. http.Error(w, "Could not retrieve file.", 500)
  467. return
  468. }
  469. }
  470. defer reader.Close()
  471. header := &tar.Header{
  472. Name: strings.Split(key, "/")[1],
  473. Size: metadata.ContentLength,
  474. }
  475. err = zw.WriteHeader(header)
  476. if err != nil {
  477. log.Printf("%s", err.Error())
  478. http.Error(w, "Internal server error.", 500)
  479. return
  480. }
  481. if _, err = io.Copy(zw, reader); err != nil {
  482. log.Printf("%s", err.Error())
  483. http.Error(w, "Internal server error.", 500)
  484. return
  485. }
  486. }
  487. }
  488. func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) {
  489. vars := mux.Vars(r)
  490. files := vars["files"]
  491. tarfilename := fmt.Sprintf("transfersh-%d.tar", uint16(time.Now().UnixNano()))
  492. w.Header().Set("Content-Type", "application/x-tar")
  493. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", tarfilename))
  494. w.Header().Set("Connection", "close")
  495. zw := tar.NewWriter(w)
  496. defer zw.Close()
  497. for _, key := range strings.Split(files, ",") {
  498. key = utils.ResolveKey(key, s.proxyPath)
  499. token := strings.Split(key, "/")[0]
  500. filename := strings.Split(key, "/")[1]
  501. if _, err := s.checkMetadata(token, filename, true); err != nil {
  502. log.Printf("Error metadata: %s", err.Error())
  503. continue
  504. }
  505. reader, metadata, err := s.storage.Get(token, filename)
  506. if err != nil {
  507. if s.storage.IsNotExist(err) {
  508. http.Error(w, "File not found", 404)
  509. return
  510. } else {
  511. log.Printf("%s", err.Error())
  512. http.Error(w, "Could not retrieve file.", 500)
  513. return
  514. }
  515. }
  516. defer reader.Close()
  517. header := &tar.Header{
  518. Name: strings.Split(key, "/")[1],
  519. Size: metadata.ContentLength,
  520. }
  521. err = zw.WriteHeader(header)
  522. if err != nil {
  523. log.Printf("%s", err.Error())
  524. http.Error(w, "Internal server error.", 500)
  525. return
  526. }
  527. if _, err = io.Copy(zw, reader); err != nil {
  528. log.Printf("%s", err.Error())
  529. http.Error(w, "Internal server error.", 500)
  530. return
  531. }
  532. }
  533. }
  534. func (s *Server) headHandler(w http.ResponseWriter, r *http.Request) {
  535. vars := mux.Vars(r)
  536. token := vars["token"]
  537. filename := vars["filename"]
  538. metadata, err := s.checkMetadata(token, filename, false)
  539. if err != nil {
  540. log.Printf("Error metadata: %s", err.Error())
  541. http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
  542. return
  543. }
  544. if s.storage.IsNotExist(err) {
  545. http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
  546. return
  547. } else if err != nil {
  548. log.Printf("%s", err.Error())
  549. http.Error(w, "Could not retrieve file.", 500)
  550. return
  551. }
  552. remainingDownloads, remainingDays := metadata.RemainingLimitHeaderValues()
  553. w.Header().Set("Content-Type", metadata.ContentType)
  554. w.Header().Set("Content-Length", strconv.FormatInt(metadata.ContentLength, 10))
  555. w.Header().Set("Connection", "close")
  556. w.Header().Set("X-Remaining-Downloads", remainingDownloads)
  557. w.Header().Set("X-Remaining-Days", remainingDays)
  558. }
  559. func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
  560. vars := mux.Vars(r)
  561. action := vars["action"]
  562. token := vars["token"]
  563. filename := vars["filename"]
  564. metadata, err := s.checkMetadata(token, filename, true)
  565. if err != nil {
  566. log.Printf("Error metadata: %s", err.Error())
  567. http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
  568. return
  569. }
  570. reader, _, err := s.storage.Get(token, filename)
  571. if s.storage.IsNotExist(err) {
  572. http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
  573. return
  574. } else if err != nil {
  575. log.Printf("%s", err.Error())
  576. http.Error(w, "Could not retrieve file.", 500)
  577. return
  578. }
  579. defer reader.Close()
  580. var disposition string
  581. if action == "inline" {
  582. disposition = "inline"
  583. } else {
  584. disposition = "attachment"
  585. }
  586. remainingDownloads, remainingDays := metadata.RemainingLimitHeaderValues()
  587. w.Header().Set("Content-Type", metadata.ContentType)
  588. w.Header().Set("Content-Length", strconv.FormatInt(metadata.ContentLength, 10))
  589. w.Header().Set("Content-Disposition", fmt.Sprintf("%s; filename=\"%s\"", disposition, filename))
  590. w.Header().Set("Connection", "keep-alive")
  591. w.Header().Set("X-Remaining-Downloads", remainingDownloads)
  592. w.Header().Set("X-Remaining-Days", remainingDays)
  593. if w.Header().Get("Range") == "" {
  594. if _, err = io.Copy(w, reader); err != nil {
  595. log.Printf("%s", err.Error())
  596. http.Error(w, "Error occurred copying to output stream", 500)
  597. return
  598. }
  599. return
  600. }
  601. file, err := ioutil.TempFile(s.tempPath, "range-")
  602. if err != nil {
  603. log.Printf("%s", err.Error())
  604. http.Error(w, "Error occurred copying to output stream", 500)
  605. return
  606. }
  607. defer utils.CleanTmpFile(file)
  608. tee := io.TeeReader(reader, file)
  609. for {
  610. b := make([]byte, _5M)
  611. _, err = tee.Read(b)
  612. if err == io.EOF {
  613. break
  614. }
  615. if err != nil {
  616. log.Printf("%s", err.Error())
  617. http.Error(w, "Error occurred copying to output stream", 500)
  618. return
  619. }
  620. }
  621. http.ServeContent(w, r, filename, time.Now(), file)
  622. }
  623. func (s *Server) scanHandler(w http.ResponseWriter, r *http.Request) {
  624. vars := mux.Vars(r)
  625. filename := utils.Sanitize(vars["filename"])
  626. contentLength := r.ContentLength
  627. contentType := r.Header.Get("Content-Type")
  628. s.logger.Printf("Scanning %s %d %s", filename, contentLength, contentType)
  629. var reader io.Reader
  630. reader = r.Body
  631. c := clamd.NewClamd(s.ClamAVDaemonHost)
  632. abort := make(chan bool)
  633. response, err := c.ScanStream(reader, abort)
  634. if err != nil {
  635. log.Printf("%s", err.Error())
  636. http.Error(w, err.Error(), 500)
  637. return
  638. }
  639. select {
  640. case s := <-response:
  641. _, _ = w.Write([]byte(fmt.Sprintf("%v\n", s.Status)))
  642. case <-time.After(time.Second * 60):
  643. abort <- true
  644. }
  645. close(abort)
  646. }
  647. func (s *Server) virusTotalHandler(w http.ResponseWriter, r *http.Request) {
  648. vars := mux.Vars(r)
  649. filename := utils.Sanitize(vars["filename"])
  650. contentLength := r.ContentLength
  651. contentType := r.Header.Get("Content-Type")
  652. s.logger.Printf("Submitting to VirusTotal: %s %d %s", filename, contentLength, contentType)
  653. vt, err := virustotal.NewVirusTotal(s.VirusTotalKey)
  654. if err != nil {
  655. http.Error(w, err.Error(), 500)
  656. return
  657. }
  658. var reader io.Reader
  659. reader = r.Body
  660. result, err := vt.Scan(filename, reader)
  661. if err != nil {
  662. http.Error(w, err.Error(), 500)
  663. return
  664. }
  665. s.logger.Println(result)
  666. _, _ = w.Write([]byte(fmt.Sprintf("%v\n", result.Permalink)))
  667. }
  668. func (s *Server) metadataForRequest(contentType string, contentLength int64, r *http.Request) storage.Metadata {
  669. metadata := storage.Metadata{
  670. ContentType: contentType,
  671. ContentLength: contentLength,
  672. MaxDate: time.Now().Add(s.lifetime),
  673. Downloads: 0,
  674. MaxDownloads: -1,
  675. DeletionToken: utils.Encode(10000000+int64(rand.Intn(1000000000))) + utils.Encode(10000000+int64(rand.Intn(1000000000))),
  676. }
  677. if v := r.Header.Get("Max-Downloads"); v == "" {
  678. } else if v, err := strconv.Atoi(v); err != nil {
  679. } else {
  680. metadata.MaxDownloads = v
  681. }
  682. if maxDays := r.Header.Get("Max-Days"); maxDays != "" {
  683. v, err := strconv.Atoi(maxDays)
  684. if err != nil {
  685. return metadata
  686. }
  687. metadata.MaxDate = time.Now().Add(time.Hour * 24 * time.Duration(v))
  688. }
  689. return metadata
  690. }
  691. func (s *Server) checkMetadata(token, filename string, increaseDownload bool) (metadata storage.Metadata, err error) {
  692. s.Lock(token, filename)
  693. defer s.Unlock(token, filename)
  694. file := fmt.Sprintf("%s/%s", token, filename)
  695. metadata, err = s.storage.Head(token, filename)
  696. if s.storage.IsNotExist(err) {
  697. return metadata, nil
  698. } else if err != nil {
  699. return metadata, err
  700. }
  701. if metadata.MaxDownloads != -1 && metadata.Downloads >= metadata.MaxDownloads {
  702. return metadata, errors.New("max downloads exceeded for " + file)
  703. } else if !metadata.MaxDate.IsZero() && time.Now().After(metadata.MaxDate) {
  704. return metadata, errors.New("file access expired for " + file)
  705. } else {
  706. // update number of downloads
  707. if increaseDownload {
  708. metadata.Downloads++
  709. }
  710. if err := s.storage.Meta(token, filename, metadata); err != nil {
  711. return metadata, errors.New("could not save metadata for " + file)
  712. }
  713. }
  714. return metadata, nil
  715. }
  716. func (s *Server) checkDeletionToken(deletionToken, token, filename string) error {
  717. s.Lock(token, filename)
  718. defer s.Unlock(token, filename)
  719. metadata, err := s.storage.Head(token, filename)
  720. if s.storage.IsNotExist(err) {
  721. return nil
  722. }
  723. if err != nil {
  724. return err
  725. }
  726. if metadata.DeletionToken != deletionToken {
  727. return errors.New("deletion token does not match")
  728. }
  729. return nil
  730. }
  731. func (s *Server) Lock(token, filename string) {
  732. key := path.Join(token, filename)
  733. if _, ok := s.locks[key]; !ok {
  734. s.locks[key] = &sync.Mutex{}
  735. }
  736. s.locks[key].Lock()
  737. }
  738. func (s *Server) Unlock(token, filename string) {
  739. key := path.Join(token, filename)
  740. s.locks[key].Unlock()
  741. }