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.
 
 
 

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