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.
 
 
 

580 lines
13 KiB

  1. /*
  2. The MIT License (MIT)
  3. Copyright (c) 2014 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 main
  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. "github.com/dutchcoders/go-clamd"
  31. "github.com/goamz/goamz/s3"
  32. "github.com/golang/gddo/httputil/header"
  33. "github.com/gorilla/mux"
  34. "github.com/kennygrant/sanitize"
  35. "io"
  36. "io/ioutil"
  37. "log"
  38. "math/rand"
  39. "mime"
  40. "net/http"
  41. "os"
  42. "path/filepath"
  43. "strconv"
  44. "strings"
  45. "time"
  46. html_template "html/template"
  47. text_template "text/template"
  48. )
  49. func healthHandler(w http.ResponseWriter, r *http.Request) {
  50. fmt.Fprintf(w, "Approaching Neutral Zone, all systems normal and functioning.")
  51. }
  52. // this handler will output html or text, depending on the
  53. // support of the client (Accept header).
  54. func viewHandler(w http.ResponseWriter, r *http.Request) {
  55. // vars := mux.Vars(r)
  56. actual := header.ParseAccept(r.Header, "Accept")
  57. html := false
  58. for _, s := range actual {
  59. if s.Value == "text/html" {
  60. html = true
  61. }
  62. }
  63. if html {
  64. tmpl, err := html_template.ParseFiles("static/index.html")
  65. if err != nil {
  66. http.Error(w, err.Error(), http.StatusInternalServerError)
  67. return
  68. }
  69. if err := tmpl.Execute(w, nil); err != nil {
  70. http.Error(w, err.Error(), http.StatusInternalServerError)
  71. return
  72. }
  73. } else {
  74. tmpl, err := text_template.ParseFiles("static/index.txt")
  75. if err != nil {
  76. http.Error(w, err.Error(), http.StatusInternalServerError)
  77. return
  78. }
  79. if err := tmpl.Execute(w, nil); err != nil {
  80. http.Error(w, err.Error(), http.StatusInternalServerError)
  81. return
  82. }
  83. }
  84. }
  85. func notFoundHandler(w http.ResponseWriter, r *http.Request) {
  86. http.Error(w, http.StatusText(404), 404)
  87. }
  88. func postHandler(w http.ResponseWriter, r *http.Request) {
  89. if err := r.ParseMultipartForm(_24K); nil != err {
  90. log.Println(err)
  91. http.Error(w, "Error occured copying to output stream", 500)
  92. return
  93. }
  94. bucket, err := getBucket()
  95. if err != nil {
  96. log.Println(err)
  97. http.Error(w, "Error occured copying to output stream", 500)
  98. return
  99. }
  100. token := Encode(10000000 + int64(rand.Intn(1000000000)))
  101. w.Header().Set("Content-Type", "text/plain")
  102. for _, fheaders := range r.MultipartForm.File {
  103. for _, fheader := range fheaders {
  104. filename := sanitize.Path(filepath.Base(fheader.Filename))
  105. contentType := fheader.Header.Get("Content-Type")
  106. if contentType == "" {
  107. contentType = mime.TypeByExtension(filepath.Ext(fheader.Filename))
  108. }
  109. var f io.Reader
  110. var err error
  111. if f, err = fheader.Open(); err != nil {
  112. log.Print(err)
  113. http.Error(w, err.Error(), 500)
  114. return
  115. }
  116. var b bytes.Buffer
  117. n, err := io.CopyN(&b, f, _24K+1)
  118. if err != nil && err != io.EOF {
  119. log.Print(err)
  120. http.Error(w, err.Error(), 500)
  121. return
  122. }
  123. var reader io.Reader
  124. if n > _24K {
  125. file, err := ioutil.TempFile(config.Temp, "transfer-")
  126. if err != nil {
  127. log.Fatal(err)
  128. }
  129. defer file.Close()
  130. n, err = io.Copy(file, io.MultiReader(&b, f))
  131. if err != nil {
  132. os.Remove(file.Name())
  133. log.Print(err)
  134. http.Error(w, err.Error(), 500)
  135. return
  136. }
  137. reader, err = os.Open(file.Name())
  138. } else {
  139. reader = bytes.NewReader(b.Bytes())
  140. }
  141. contentLength := n
  142. key := fmt.Sprintf("%s/%s", token, filename)
  143. log.Printf("Uploading %s %d %s", key, contentLength, contentType)
  144. if err = bucket.PutReader(key, reader, contentLength, contentType, s3.PublicRead, s3.Options{}); err != nil {
  145. log.Print(err)
  146. http.Error(w, err.Error(), 500)
  147. return
  148. }
  149. fmt.Fprintf(w, "https://transfer.sh/%s\n", key)
  150. }
  151. }
  152. }
  153. func scanHandler(w http.ResponseWriter, r *http.Request) {
  154. vars := mux.Vars(r)
  155. filename := sanitize.Path(filepath.Base(vars["filename"]))
  156. contentLength := r.ContentLength
  157. contentType := r.Header.Get("Content-Type")
  158. log.Printf("Scanning %s %d %s", filename, contentLength, contentType)
  159. var reader io.Reader
  160. reader = r.Body
  161. c := clamd.NewClamd("/tmp/clamd.socket")
  162. response, err := c.ScanStream(reader)
  163. if err != nil {
  164. http.Error(w, err.Error(), 500)
  165. }
  166. var b string
  167. for s := range response {
  168. b += s
  169. if !strings.HasPrefix(s, "stream: ") {
  170. continue
  171. }
  172. w.Write([]byte(fmt.Sprintf("%v\n", s[8:])))
  173. }
  174. }
  175. func putHandler(w http.ResponseWriter, r *http.Request) {
  176. vars := mux.Vars(r)
  177. filename := sanitize.Path(filepath.Base(vars["filename"]))
  178. contentLength := r.ContentLength
  179. var reader io.Reader
  180. reader = r.Body
  181. if contentLength == -1 {
  182. // queue file to disk, because s3 needs content length
  183. var err error
  184. var f io.Reader
  185. f = reader
  186. var b bytes.Buffer
  187. n, err := io.CopyN(&b, f, _24K+1)
  188. if err != nil && err != io.EOF {
  189. log.Print(err)
  190. http.Error(w, err.Error(), 500)
  191. return
  192. }
  193. if n > _24K {
  194. file, err := ioutil.TempFile(config.Temp, "transfer-")
  195. if err != nil {
  196. log.Print(err)
  197. http.Error(w, err.Error(), 500)
  198. return
  199. }
  200. defer file.Close()
  201. n, err = io.Copy(file, io.MultiReader(&b, f))
  202. if err != nil {
  203. os.Remove(file.Name())
  204. log.Print(err)
  205. http.Error(w, err.Error(), 500)
  206. return
  207. }
  208. reader, err = os.Open(file.Name())
  209. } else {
  210. reader = bytes.NewReader(b.Bytes())
  211. }
  212. contentLength = n
  213. }
  214. contentType := r.Header.Get("Content-Type")
  215. if contentType == "" {
  216. contentType = mime.TypeByExtension(filepath.Ext(vars["filename"]))
  217. }
  218. key := fmt.Sprintf("%s/%s", Encode(10000000+int64(rand.Intn(1000000000))), filename)
  219. log.Printf("Uploading %s %d %s", key, contentLength, contentType)
  220. var b *s3.Bucket
  221. var err error
  222. b, err = getBucket()
  223. if err != nil {
  224. http.Error(w, errors.New("Could not open bucket").Error(), 500)
  225. return
  226. }
  227. err = b.PutReader(key, reader, contentLength, contentType, s3.PublicRead, s3.Options{})
  228. if err != nil {
  229. http.Error(w, errors.New("Could not save file").Error(), 500)
  230. return
  231. }
  232. // w.Statuscode = 200
  233. w.Header().Set("Content-Type", "text/plain")
  234. fmt.Fprintf(w, "https://transfer.sh/%s\n", key)
  235. }
  236. func zipHandler(w http.ResponseWriter, r *http.Request) {
  237. vars := mux.Vars(r)
  238. files := vars["files"]
  239. b, err := getBucket()
  240. if err != nil {
  241. http.Error(w, err.Error(), 500)
  242. return
  243. }
  244. filename := fmt.Sprintf("transfersh-%d.zip", uint16(time.Now().UnixNano()))
  245. w.Header().Set("Content-Type", "application/zip")
  246. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
  247. w.Header().Set("Connection", "close")
  248. zw := zip.NewWriter(w)
  249. for _, key := range strings.Split(files, ",") {
  250. rc, err := b.GetResponse(key)
  251. if err != nil {
  252. if err.Error() == "The specified key does not exist." {
  253. http.Error(w, "File not found", 404)
  254. return
  255. } else {
  256. log.Printf("%s", err.Error())
  257. http.Error(w, "Could not retrieve file.", 500)
  258. return
  259. }
  260. }
  261. defer rc.Body.Close()
  262. header := &zip.FileHeader{
  263. Name: strings.Split(key, "/")[1],
  264. Method: zip.Store,
  265. ModifiedTime: uint16(time.Now().UnixNano()),
  266. ModifiedDate: uint16(time.Now().UnixNano()),
  267. }
  268. fi := rc.Body
  269. fw, err := zw.CreateHeader(header)
  270. if err != nil {
  271. log.Printf("%s", err.Error())
  272. http.Error(w, "Internal server error.", 500)
  273. return
  274. }
  275. if _, err = io.Copy(fw, fi); err != nil {
  276. log.Printf("%s", err.Error())
  277. http.Error(w, "Internal server error.", 500)
  278. return
  279. }
  280. }
  281. if err := zw.Close(); err != nil {
  282. log.Printf("%s", err.Error())
  283. http.Error(w, "Internal server error.", 500)
  284. return
  285. }
  286. }
  287. func tarGzHandler(w http.ResponseWriter, r *http.Request) {
  288. vars := mux.Vars(r)
  289. files := vars["files"]
  290. b, err := getBucket()
  291. if err != nil {
  292. http.Error(w, err.Error(), 500)
  293. return
  294. }
  295. filename := fmt.Sprintf("transfersh-%d.tar.gz", uint16(time.Now().UnixNano()))
  296. w.Header().Set("Content-Type", "application/x-gzip")
  297. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
  298. w.Header().Set("Connection", "close")
  299. os := gzip.NewWriter(w)
  300. defer os.Close()
  301. zw := tar.NewWriter(os)
  302. defer zw.Close()
  303. for _, key := range strings.Split(files, ",") {
  304. rc, err := b.GetResponse(key)
  305. if err != nil {
  306. if err.Error() == "The specified key does not exist." {
  307. http.Error(w, "File not found", 404)
  308. return
  309. } else {
  310. log.Printf("%s", err.Error())
  311. http.Error(w, "Could not retrieve file.", 500)
  312. return
  313. }
  314. }
  315. defer rc.Body.Close()
  316. contentLength, err := strconv.Atoi(rc.Header.Get("Content-Length"))
  317. header := &tar.Header{
  318. Name: strings.Split(key, "/")[1],
  319. Size: int64(contentLength),
  320. }
  321. err = zw.WriteHeader(header)
  322. if err != nil {
  323. log.Printf("%s", err.Error())
  324. http.Error(w, "Internal server error.", 500)
  325. return
  326. }
  327. fi := rc.Body
  328. if _, err = io.Copy(zw, fi); err != nil {
  329. log.Printf("%s", err.Error())
  330. http.Error(w, "Internal server error.", 500)
  331. return
  332. }
  333. }
  334. }
  335. func tarHandler(w http.ResponseWriter, r *http.Request) {
  336. vars := mux.Vars(r)
  337. files := vars["files"]
  338. b, err := getBucket()
  339. if err != nil {
  340. http.Error(w, err.Error(), 500)
  341. return
  342. }
  343. filename := fmt.Sprintf("transfersh-%d.tar", uint16(time.Now().UnixNano()))
  344. w.Header().Set("Content-Type", "application/x-tar")
  345. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
  346. w.Header().Set("Connection", "close")
  347. zw := tar.NewWriter(w)
  348. defer zw.Close()
  349. for _, key := range strings.Split(files, ",") {
  350. rc, err := b.GetResponse(key)
  351. if err != nil {
  352. if err.Error() == "The specified key does not exist." {
  353. http.Error(w, "File not found", 404)
  354. return
  355. } else {
  356. log.Printf("%s", err.Error())
  357. http.Error(w, "Could not retrieve file.", 500)
  358. return
  359. }
  360. }
  361. defer rc.Body.Close()
  362. contentLength, err := strconv.Atoi(rc.Header.Get("Content-Length"))
  363. header := &tar.Header{
  364. Name: strings.Split(key, "/")[1],
  365. Size: int64(contentLength),
  366. }
  367. err = zw.WriteHeader(header)
  368. if err != nil {
  369. log.Printf("%s", err.Error())
  370. http.Error(w, "Internal server error.", 500)
  371. return
  372. }
  373. fi := rc.Body
  374. if _, err = io.Copy(zw, fi); err != nil {
  375. log.Printf("%s", err.Error())
  376. http.Error(w, "Internal server error.", 500)
  377. return
  378. }
  379. }
  380. }
  381. func getHandler(w http.ResponseWriter, r *http.Request) {
  382. vars := mux.Vars(r)
  383. token := vars["token"]
  384. filename := vars["filename"]
  385. key := fmt.Sprintf("%s/%s", token, filename)
  386. b, err := getBucket()
  387. if err != nil {
  388. http.Error(w, err.Error(), 500)
  389. return
  390. }
  391. rc, err := b.GetResponse(key)
  392. if err != nil {
  393. if err.Error() == "The specified key does not exist." {
  394. http.Error(w, "File not found", 404)
  395. return
  396. } else {
  397. log.Printf("%s", err.Error())
  398. http.Error(w, "Could not retrieve file.", 500)
  399. return
  400. }
  401. }
  402. defer rc.Body.Close()
  403. contentType := rc.Header.Get("Content-Type")
  404. w.Header().Set("Content-Type", contentType)
  405. mediaType, _, _ := mime.ParseMediaType(contentType)
  406. fmt.Println(mediaType)
  407. switch {
  408. case mediaType == "text/html":
  409. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
  410. break
  411. case strings.HasPrefix(mediaType, "text"):
  412. case mediaType == "":
  413. break
  414. default:
  415. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
  416. }
  417. w.Header().Set("Connection", "close")
  418. if _, err = io.Copy(w, rc.Body); err != nil {
  419. http.Error(w, "Error occured copying to output stream", 500)
  420. return
  421. }
  422. }
  423. func RedirectHandler(h http.Handler) http.HandlerFunc {
  424. return func(w http.ResponseWriter, r *http.Request) {
  425. if ipAddrFromRemoteAddr(r.Host) != "transfer.sh" && ipAddrFromRemoteAddr(r.Host) != "127.0.0.1" && r.URL.Path != "/health.html" {
  426. http.Redirect(w, r, "https://transfer.sh/", 301)
  427. return
  428. }
  429. if ipAddrFromRemoteAddr(r.Host) == "transfer.sh" && r.Header.Get("X-Forwarded-Proto") != "https" && r.Method == "GET" {
  430. http.Redirect(w, r, "https://transfer.sh/", 301)
  431. return
  432. }
  433. h.ServeHTTP(w, r)
  434. }
  435. }
  436. // Create a log handler for every request it receives.
  437. func LoveHandler(h http.Handler) http.HandlerFunc {
  438. return func(w http.ResponseWriter, r *http.Request) {
  439. w.Header().Set("x-made-with", "<3 by DutchCoders")
  440. w.Header().Set("x-served-by", "Proudly served by DutchCoders")
  441. w.Header().Set("Server", "Transfer.sh HTTP Server 1.0")
  442. h.ServeHTTP(w, r)
  443. }
  444. }