Module: InstagramConnect::Media::Attaching

Defined in:
lib/instagram_connect/media/attaching.rb

Overview

Turns fetched bytes into a stored file, or into an honest reason why not.

The rule that matters here: when the declared Content-Type and the actual magic bytes disagree, the magic bytes win. A Content-Type header is metadata anyone can set; the first few bytes of the file are what the file actually is. Trusting the header is how a host ends up serving an HTML document from an tag.

Defined Under Namespace

Classes: Result

Class Method Summary collapse

Class Method Details

.allowed?(mime, config) ⇒ Boolean

Returns:

  • (Boolean)


41
42
43
44
# File 'lib/instagram_connect/media/attaching.rb', line 41

def allowed?(mime, config)
  allowed = config.allowed_media_types
  allowed.blank? || allowed.include?(mime)
end

.evaluate(body:, declared_mime:, filename: nil, config: InstagramConnect.configuration) ⇒ Object



20
21
22
23
24
25
26
27
28
29
# File 'lib/instagram_connect/media/attaching.rb', line 20

def evaluate(body:, declared_mime:, filename: nil, config: InstagramConnect.configuration)
  bytes = body.to_s
  return failure("empty") if bytes.empty?

  mime = sniff(bytes, filename: filename, declared: declared_mime)
  return failure("type_not_allowed:#{mime}") unless allowed?(mime, config)
  return failure("too_large:#{bytes.bytesize}") unless within_size?(mime, bytes.bytesize, config)

  Result.new(ok: true, mime: mime, size: bytes.bytesize)
end

.failure(reason) ⇒ Object



52
53
54
# File 'lib/instagram_connect/media/attaching.rb', line 52

def failure(reason)
  Result.new(ok: false, error: reason)
end

.sniff(bytes, filename: nil, declared: nil) ⇒ Object



31
32
33
34
35
36
37
38
39
# File 'lib/instagram_connect/media/attaching.rb', line 31

def sniff(bytes, filename: nil, declared: nil)
  sniffed = ::Marcel::MimeType.for(StringIO.new(bytes), name: filename)
  # Marcel falls back to this when it recognises nothing, in which case the
  # declared type is the only information available and is better than
  # nothing.
  return declared.to_s.split(";").first.to_s.strip if sniffed == "application/octet-stream" && declared.present?

  sniffed
end

.within_size?(mime, bytes, config) ⇒ Boolean

Both Meta's per-type ceiling and the host's own cap have to hold. The host's exists so an adopter can be stricter than Meta, never looser.

Returns:

  • (Boolean)


48
49
50
# File 'lib/instagram_connect/media/attaching.rb', line 48

def within_size?(mime, bytes, config)
  Limits.within_size?(mime, bytes) && bytes <= config.media_max_bytes.to_i
end