Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 

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