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.

83 lines
1.9 KiB

  1. package helpers
  2. import (
  3. "bytes"
  4. "encoding/hex"
  5. "io"
  6. "unicode"
  7. "github.com/andreimarcu/linx-server/backends"
  8. "github.com/minio/sha256-simd"
  9. "gopkg.in/h2non/filetype.v1"
  10. )
  11. func GenerateMetadata(r io.Reader) (m backends.Metadata, err error) {
  12. // Since we don't have the ability to seek within a file, we can use a
  13. // Buffer in combination with a TeeReader to keep a copy of the bytes
  14. // we read when detecting the file type. These bytes are still needed
  15. // to hash the file and determine its size and cannot be discarded.
  16. var buf bytes.Buffer
  17. teeReader := io.TeeReader(r, &buf)
  18. // Get first 512 bytes for mimetype detection
  19. header := make([]byte, 512)
  20. _, err = teeReader.Read(header)
  21. if err != nil {
  22. return
  23. }
  24. // Create a Hash and a MultiReader that includes the Buffer we created
  25. // above along with the original Reader, which will have the rest of
  26. // the file.
  27. hasher := sha256.New()
  28. multiReader := io.MultiReader(&buf, r)
  29. // Copy everything into the Hash, then use the number of bytes written
  30. // as the file size.
  31. var readLen int64
  32. readLen, err = io.Copy(hasher, multiReader)
  33. if err != nil {
  34. return
  35. } else {
  36. m.Size += readLen
  37. }
  38. // Get the hex-encoded string version of the Hash checksum
  39. m.Sha256sum = hex.EncodeToString(hasher.Sum(nil))
  40. // Use the bytes we extracted earlier and attempt to determine the file
  41. // type
  42. kind, err := filetype.Match(header)
  43. if err != nil {
  44. m.Mimetype = "application/octet-stream"
  45. return m, err
  46. } else if kind.MIME.Value != "" {
  47. m.Mimetype = kind.MIME.Value
  48. } else if printable(header) {
  49. m.Mimetype = "text/plain"
  50. } else {
  51. m.Mimetype = "application/octet-stream"
  52. }
  53. return
  54. }
  55. func printable(data []byte) bool {
  56. for i, b := range data {
  57. r := rune(b)
  58. // A null terminator that's not at the beginning of the file
  59. if r == 0 && i == 0 {
  60. return false
  61. } else if r == 0 && i < 0 {
  62. continue
  63. }
  64. if r > unicode.MaxASCII {
  65. return false
  66. }
  67. }
  68. return true
  69. }