Module: Ruby::Rego::Builtins::Codecs::JsonSchema::Formats::Rfc2047

Defined in:
lib/ruby/rego/builtins/codecs/json_schema_email.rb

Overview

The slice of Go’s net/mail decodeRFC2047Word + mime.WordDecoder.Decode that MailAddress’ consume_phrase needs (mirroring Go’s net/mail -> mime package boundary). All pure byte functions: an encoded-word is structurally =?charset?encoding?text?= (>=8 bytes, exactly four ?, non-empty charset, one-byte encoding) with a decodable B/Q payload; only utf-8/iso-8859-1/us-ascii convert natively, anything else errors. Byte-oriented to mirror Go (len / index / base64 / qDecode on bytes). See the differential coverage in the email spec. :reek:UncommunicativeModuleName – “Rfc2047” is the RFC number, the canonical name for this spec.

Constant Summary collapse

CHARSETS =

Charsets mime.WordDecoder.convert handles without a CharsetReader (EqualFold, i.e. ASCII-case).

%w[utf-8 iso-8859-1 us-ascii].freeze

Class Method Summary collapse

Class Method Details

.base64_char?(byte) ⇒ Boolean

:reek:NilCheck

Returns:

  • (Boolean)


474
475
476
477
478
479
# File 'lib/ruby/rego/builtins/codecs/json_schema_email.rb', line 474

def self.base64_char?(byte)
  return false if byte.nil?

  (0x41..0x5A).cover?(byte) || (0x61..0x7A).cover?(byte) || (0x30..0x39).cover?(byte) ||
    byte == 0x2B || byte == 0x2F # '+' '/'
end

.base64_decodes?(text) ⇒ Boolean

base64.StdEncoding.DecodeString success boundary: length a multiple of 4, only alphabet bytes with 0-2 trailing = padding. Empty text decodes to empty (success). rubocop:disable Metrics/MethodLength :reek:TooManyStatements

Returns:

  • (Boolean)


428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
# File 'lib/ruby/rego/builtins/codecs/json_schema_email.rb', line 428

def self.base64_decodes?(text)
  bytes = text.b
  size = bytes.bytesize
  return true if size.zero?
  return false unless (size % 4).zero?

  pad = 0
  pad += 1 while pad < 2 && bytes.getbyte(size - 1 - pad) == 0x3D

  # Scan the body bytes in place (no substring / no Array#bytes allocation) — like qdecodes?.
  index = 0
  body = size - pad
  while index < body
    return false unless base64_char?(bytes.getbyte(index))

    index += 1
  end
  true
end

.hex_byte?(byte) ⇒ Boolean

:reek:NilCheck

Returns:

  • (Boolean)


491
492
493
494
495
# File 'lib/ruby/rego/builtins/codecs/json_schema_email.rb', line 491

def self.hex_byte?(byte)
  return false if byte.nil?

  (0x30..0x39).cover?(byte) || (0x41..0x46).cover?(byte) || (0x61..0x66).cover?(byte)
end

.payload_decodes?(encoding, text) ⇒ Boolean

mime decode: ‘B’/’b’ = base64.StdEncoding, ‘Q’/’q’ = qDecode. Any other encoding byte errors. :reek:ControlParameter – dispatches on the encoding byte, like Go’s decode(encoding, text).

Returns:

  • (Boolean)


416
417
418
419
420
421
422
# File 'lib/ruby/rego/builtins/codecs/json_schema_email.rb', line 416

def self.payload_decodes?(encoding, text)
  case encoding
  when "B", "b" then base64_decodes?(text)
  when "Q", "q" then qdecodes?(text)
  else false
  end
end

.qdecodes?(text) ⇒ Boolean

Port of mime.qDecode’s per-byte acceptance: _, an =XX hex escape (needs two hex digits), printable ASCII, or TAB/LF/CR. Any other byte errors. Returns whether the whole run decodes. rubocop:disable Metrics/MethodLength :reek:TooManyStatements :reek:DuplicateMethodCall

Returns:

  • (Boolean)


453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
# File 'lib/ruby/rego/builtins/codecs/json_schema_email.rb', line 453

def self.qdecodes?(text)
  bytes = text.b
  index = 0
  size = bytes.bytesize
  while index < size
    byte = bytes.getbyte(index)
    if byte == 0x3D # '='
      return false if index + 2 >= size
      return false unless hex_byte?(bytes.getbyte(index + 1)) && hex_byte?(bytes.getbyte(index + 2))

      index += 2
    elsif !qtext_byte?(byte)
      return false
    end
    index += 1
  end
  true
end

.qtext_byte?(byte) ⇒ Boolean

qDecode-acceptable raw byte: ‘_’ or printable ASCII (0x20..0x7E) or TAB/LF/CR. nil (an out-of-bounds read) is never acceptable. :reek:NilCheck

Returns:

  • (Boolean)


484
485
486
487
488
# File 'lib/ruby/rego/builtins/codecs/json_schema_email.rb', line 484

def self.qtext_byte?(byte)
  return false if byte.nil?

  byte == 0x5F || (0x20..0x7E).cover?(byte) || byte == 0x09 || byte == 0x0A || byte == 0x0D
end

.status(word) ⇒ Object

How consume_phrase must treat an atom word, mirroring Go’s decodeRFC2047Word isEncoded/err: :charset_error - a real encoded-word whose payload decodes but whose charset mime can’t convert (Go’s Decode errors); :empty - a real encoded-word with an empty payload, decoding to “” (isEncoded, dropped); :encoded - a real encoded-word with non-empty content (isEncoded); :raw - anything else: a plain atom, a malformed encoded-word, or one whose B/Q payload fails to decode — Go keeps the raw text (isEncoded=false). rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity :reek:TooManyStatements :reek:DuplicateMethodCall



396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
# File 'lib/ruby/rego/builtins/codecs/json_schema_email.rb', line 396

def self.status(word)
  bytes = word.b
  return :raw if bytes.bytesize < 8 || !bytes.start_with?("=?") || !bytes.end_with?("?=")
  return :raw unless bytes.count("?") == 4

  charset, rest = bytes[2...-2].to_s.split("?", 2)
  encoding, text = rest.to_s.split("?", 2)
  return :raw if charset.to_s.empty? || encoding.to_s.bytesize != 1
  return :raw unless payload_decodes?(encoding.to_s, text.to_s)
  return :charset_error unless CHARSETS.include?(charset.to_s.downcase)

  # Emptiness of the raw text is exact: no NON-empty payload that passes payload_decodes? can
  # decode to "" (min base64 quantum is 4 chars -> >=1 byte; qDecode never deletes bytes), so we
  # need not actually decode to know the content is empty.
  text.to_s.empty? ? :empty : :encoded
end