Contains the Concourse pipeline definition for building a line-server container
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.

222 lines
4.7 KiB

  1. package main
  2. import (
  3. "archive/tar"
  4. "archive/zip"
  5. "bytes"
  6. "compress/bzip2"
  7. "compress/gzip"
  8. "crypto/sha256"
  9. "encoding/hex"
  10. "encoding/json"
  11. "errors"
  12. "io"
  13. "sort"
  14. "time"
  15. "unicode"
  16. "github.com/dchest/uniuri"
  17. "gopkg.in/h2non/filetype.v1"
  18. )
  19. type MetadataJSON struct {
  20. DeleteKey string `json:"delete_key"`
  21. Sha256sum string `json:"sha256sum"`
  22. Mimetype string `json:"mimetype"`
  23. Size int64 `json:"size"`
  24. Expiry int64 `json:"expiry"`
  25. ArchiveFiles []string `json:"archive_files,omitempty"`
  26. ShortURL string `json:"short_url"`
  27. }
  28. type Metadata struct {
  29. DeleteKey string
  30. Sha256sum string
  31. Mimetype string
  32. Size int64
  33. Expiry time.Time
  34. ArchiveFiles []string
  35. ShortURL string
  36. }
  37. var NotFoundErr = errors.New("File not found.")
  38. var BadMetadata = errors.New("Corrupted metadata.")
  39. func generateMetadata(fName string, exp time.Time, delKey string) (m Metadata, err error) {
  40. file, err := fileBackend.Open(fName)
  41. if err != nil {
  42. return
  43. }
  44. defer file.Close()
  45. m.Size, err = fileBackend.Size(fName)
  46. if err != nil {
  47. return
  48. }
  49. m.Expiry = exp
  50. if delKey == "" {
  51. m.DeleteKey = uniuri.NewLen(30)
  52. } else {
  53. m.DeleteKey = delKey
  54. }
  55. // Get first 512 bytes for mimetype detection
  56. header := make([]byte, 512)
  57. file.Read(header)
  58. kind, err := filetype.Match(header)
  59. if err != nil {
  60. m.Mimetype = "application/octet-stream"
  61. } else {
  62. m.Mimetype = kind.MIME.Value
  63. }
  64. if m.Mimetype == "" {
  65. // Check if the file seems anything like text
  66. if printable(header) {
  67. m.Mimetype = "text/plain"
  68. } else {
  69. m.Mimetype = "application/octet-stream"
  70. }
  71. }
  72. // Compute the sha256sum
  73. hasher := sha256.New()
  74. file.Seek(0, 0)
  75. _, err = io.Copy(hasher, file)
  76. if err == nil {
  77. m.Sha256sum = hex.EncodeToString(hasher.Sum(nil))
  78. }
  79. file.Seek(0, 0)
  80. // If archive, grab list of filenames
  81. if m.Mimetype == "application/x-tar" {
  82. tReadr := tar.NewReader(file)
  83. for {
  84. hdr, err := tReadr.Next()
  85. if err == io.EOF || err != nil {
  86. break
  87. }
  88. if hdr.Typeflag == tar.TypeDir || hdr.Typeflag == tar.TypeReg {
  89. m.ArchiveFiles = append(m.ArchiveFiles, hdr.Name)
  90. }
  91. }
  92. sort.Strings(m.ArchiveFiles)
  93. } else if m.Mimetype == "application/x-gzip" {
  94. gzf, err := gzip.NewReader(file)
  95. if err == nil {
  96. tReadr := tar.NewReader(gzf)
  97. for {
  98. hdr, err := tReadr.Next()
  99. if err == io.EOF || err != nil {
  100. break
  101. }
  102. if hdr.Typeflag == tar.TypeDir || hdr.Typeflag == tar.TypeReg {
  103. m.ArchiveFiles = append(m.ArchiveFiles, hdr.Name)
  104. }
  105. }
  106. sort.Strings(m.ArchiveFiles)
  107. }
  108. } else if m.Mimetype == "application/x-bzip" {
  109. bzf := bzip2.NewReader(file)
  110. tReadr := tar.NewReader(bzf)
  111. for {
  112. hdr, err := tReadr.Next()
  113. if err == io.EOF || err != nil {
  114. break
  115. }
  116. if hdr.Typeflag == tar.TypeDir || hdr.Typeflag == tar.TypeReg {
  117. m.ArchiveFiles = append(m.ArchiveFiles, hdr.Name)
  118. }
  119. }
  120. sort.Strings(m.ArchiveFiles)
  121. } else if m.Mimetype == "application/zip" {
  122. zf, err := zip.NewReader(file, m.Size)
  123. if err == nil {
  124. for _, f := range zf.File {
  125. m.ArchiveFiles = append(m.ArchiveFiles, f.Name)
  126. }
  127. }
  128. sort.Strings(m.ArchiveFiles)
  129. }
  130. return
  131. }
  132. func metadataWrite(filename string, metadata *Metadata) error {
  133. mjson := MetadataJSON{}
  134. mjson.DeleteKey = metadata.DeleteKey
  135. mjson.Mimetype = metadata.Mimetype
  136. mjson.ArchiveFiles = metadata.ArchiveFiles
  137. mjson.Sha256sum = metadata.Sha256sum
  138. mjson.Expiry = metadata.Expiry.Unix()
  139. mjson.Size = metadata.Size
  140. mjson.ShortURL = metadata.ShortURL
  141. byt, err := json.Marshal(mjson)
  142. if err != nil {
  143. return err
  144. }
  145. if _, err := metaBackend.Put(filename, bytes.NewBuffer(byt)); err != nil {
  146. return err
  147. }
  148. return nil
  149. }
  150. func metadataRead(filename string) (metadata Metadata, err error) {
  151. b, err := metaBackend.Get(filename)
  152. if err != nil {
  153. // Metadata does not exist, generate one
  154. newMData, err := generateMetadata(filename, neverExpire, "")
  155. if err != nil {
  156. return metadata, err
  157. }
  158. metadataWrite(filename, &newMData)
  159. b, err = metaBackend.Get(filename)
  160. if err != nil {
  161. return metadata, BadMetadata
  162. }
  163. }
  164. mjson := MetadataJSON{}
  165. err = json.Unmarshal(b, &mjson)
  166. if err != nil {
  167. return metadata, BadMetadata
  168. }
  169. metadata.DeleteKey = mjson.DeleteKey
  170. metadata.Mimetype = mjson.Mimetype
  171. metadata.ArchiveFiles = mjson.ArchiveFiles
  172. metadata.Sha256sum = mjson.Sha256sum
  173. metadata.Expiry = time.Unix(mjson.Expiry, 0)
  174. metadata.Size = mjson.Size
  175. metadata.ShortURL = mjson.ShortURL
  176. return
  177. }
  178. func printable(data []byte) bool {
  179. for i, b := range data {
  180. r := rune(b)
  181. // A null terminator that's not at the beginning of the file
  182. if r == 0 && i == 0 {
  183. return false
  184. } else if r == 0 && i < 0 {
  185. continue
  186. }
  187. if r > unicode.MaxASCII {
  188. return false
  189. }
  190. }
  191. return true
  192. }