# frozen_string_literal: true module Mail module Jdec class Decoder class << self def decode_if_needed(text) return text if text.nil? || !text.respond_to?(:encoding) || text.encoding == Encoding::UTF_8 detected = Detector.detect(text) if detected charset = detected[:encoding].to_s text = Mail::Encodings.transcode_charset(text.dup.force_encoding(charset), charset, 'utf-8') else text = Mail::Encodings.transcode_charset(text, text.encoding, 'utf-8') end text end def force_utf8(str) str.dup.force_encoding('utf-8').encode('utf-8', invalid: :replace, undef: :replace) end def decode_utf7(str) str.gsub(/\+([^-]+)?-/n) do if $1 ($1.tr(",", "/") + "===").unpack("m")[0].encode(Encoding::UTF_8, Encoding::UTF_16BE) else '+' end end end end end class ValueDecoder class << self include Mail::Constants def value_decode(str) # Optimization: If there's no encoded-words in the string, just return it return str unless str =~ ENCODED_VALUE lines = Mail::Encodings.send(:collapse_adjacent_encodings, str) lines = lines.chunk do |value| if value =~ ENCODED_VALUE $1 else '' end end # Split on white-space boundaries with capture, so we capture the white-space as well lines.map do |charset, values| content = values.join if content =~ ENCODED_VALUE bytes = content.scan(/\=\?([^?]+)\?([QB])\?([^?]*?)\?\=/mi).map do |_, encoding, encoded| case encoding when *B_VALUES then Mail::Utilities.decode_base64(encoded) when *Q_VALUES then Mail::Encodings::QuotedPrintable.decode(encoded.gsub(/_/, '=20').sub(/\=$/, '')) end end.join('') Mail::Encodings.transcode_charset(bytes, charset) else content end end.join('') end end end end end