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.

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