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.

217 lines
4.6 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. "bitbucket.org/taruti/mimemagic"
  17. "github.com/dchest/uniuri"
  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. m.Mimetype = mimemagic.Match("", header)
  59. if m.Mimetype == "" {
  60. // Check if the file seems anything like text
  61. if printable(header) {
  62. m.Mimetype = "text/plain"
  63. } else {
  64. m.Mimetype = "application/octet-stream"
  65. }
  66. }
  67. // Compute the sha256sum
  68. hasher := sha256.New()
  69. file.Seek(0, 0)
  70. _, err = io.Copy(hasher, file)
  71. if err == nil {
  72. m.Sha256sum = hex.EncodeToString(hasher.Sum(nil))
  73. }
  74. file.Seek(0, 0)
  75. // If archive, grab list of filenames
  76. if m.Mimetype == "application/x-tar" {
  77. tReadr := tar.NewReader(file)
  78. for {
  79. hdr, err := tReadr.Next()
  80. if err == io.EOF || err != nil {
  81. break
  82. }
  83. if hdr.Typeflag == tar.TypeDir || hdr.Typeflag == tar.TypeReg {
  84. m.ArchiveFiles = append(m.ArchiveFiles, hdr.Name)
  85. }
  86. }
  87. sort.Strings(m.ArchiveFiles)
  88. } else if m.Mimetype == "application/x-gzip" {
  89. gzf, err := gzip.NewReader(file)
  90. if err == nil {
  91. tReadr := tar.NewReader(gzf)
  92. for {
  93. hdr, err := tReadr.Next()
  94. if err == io.EOF || err != nil {
  95. break
  96. }
  97. if hdr.Typeflag == tar.TypeDir || hdr.Typeflag == tar.TypeReg {
  98. m.ArchiveFiles = append(m.ArchiveFiles, hdr.Name)
  99. }
  100. }
  101. sort.Strings(m.ArchiveFiles)
  102. }
  103. } else if m.Mimetype == "application/x-bzip" {
  104. bzf := bzip2.NewReader(file)
  105. tReadr := tar.NewReader(bzf)
  106. for {
  107. hdr, err := tReadr.Next()
  108. if err == io.EOF || err != nil {
  109. break
  110. }
  111. if hdr.Typeflag == tar.TypeDir || hdr.Typeflag == tar.TypeReg {
  112. m.ArchiveFiles = append(m.ArchiveFiles, hdr.Name)
  113. }
  114. }
  115. sort.Strings(m.ArchiveFiles)
  116. } else if m.Mimetype == "application/zip" {
  117. zf, err := zip.NewReader(file, m.Size)
  118. if err == nil {
  119. for _, f := range zf.File {
  120. m.ArchiveFiles = append(m.ArchiveFiles, f.Name)
  121. }
  122. }
  123. sort.Strings(m.ArchiveFiles)
  124. }
  125. return
  126. }
  127. func metadataWrite(filename string, metadata *Metadata) error {
  128. mjson := MetadataJSON{}
  129. mjson.DeleteKey = metadata.DeleteKey
  130. mjson.Mimetype = metadata.Mimetype
  131. mjson.ArchiveFiles = metadata.ArchiveFiles
  132. mjson.Sha256sum = metadata.Sha256sum
  133. mjson.Expiry = metadata.Expiry.Unix()
  134. mjson.Size = metadata.Size
  135. mjson.ShortURL = metadata.ShortURL
  136. byt, err := json.Marshal(mjson)
  137. if err != nil {
  138. return err
  139. }
  140. if _, err := metaBackend.Put(filename, bytes.NewBuffer(byt)); err != nil {
  141. return err
  142. }
  143. return nil
  144. }
  145. func metadataRead(filename string) (metadata Metadata, err error) {
  146. b, err := metaBackend.Get(filename)
  147. if err != nil {
  148. // Metadata does not exist, generate one
  149. newMData, err := generateMetadata(filename, neverExpire, "")
  150. if err != nil {
  151. return metadata, err
  152. }
  153. metadataWrite(filename, &newMData)
  154. b, err = metaBackend.Get(filename)
  155. if err != nil {
  156. return metadata, BadMetadata
  157. }
  158. }
  159. mjson := MetadataJSON{}
  160. err = json.Unmarshal(b, &mjson)
  161. if err != nil {
  162. return metadata, BadMetadata
  163. }
  164. metadata.DeleteKey = mjson.DeleteKey
  165. metadata.Mimetype = mjson.Mimetype
  166. metadata.ArchiveFiles = mjson.ArchiveFiles
  167. metadata.Sha256sum = mjson.Sha256sum
  168. metadata.Expiry = time.Unix(mjson.Expiry, 0)
  169. metadata.Size = mjson.Size
  170. metadata.ShortURL = mjson.ShortURL
  171. return
  172. }
  173. func printable(data []byte) bool {
  174. for i, b := range data {
  175. r := rune(b)
  176. // A null terminator that's not at the beginning of the file
  177. if r == 0 && i == 0 {
  178. return false
  179. } else if r == 0 && i < 0 {
  180. continue
  181. }
  182. if r > unicode.MaxASCII {
  183. return false
  184. }
  185. }
  186. return true
  187. }