gitlab-org--gitlab-foss/config/initializers/mail_encoding_patch.rb

85 lines
2.9 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
# Monkey patch mail 2.7.1 to fix quoted-printable issues with newlines
# The issues upstream invalidate SMIME signatures under some conditions
# This was working properly in 2.6.6
#
# See https://gitlab.com/gitlab-org/gitlab/issues/197386
# See https://github.com/mikel/mail/issues/1190
module Mail
module Encodings
# PATCH
# This reverts https://github.com/mikel/mail/pull/1113, which solves some
# encoding issues with binary attachments encoded in quoted-printable, but
# unfortunately breaks re-encoding of messages
class QuotedPrintable < SevenBit
def self.decode(str)
::Mail::Utilities.to_lf str.gsub(/(?:=0D=0A|=0D|=0A)\r\n/, "\r\n").unpack1("M*")
end
def self.encode(str)
::Mail::Utilities.to_crlf([::Mail::Utilities.to_lf(str)].pack("M"))
end
end
end
class Body
def encoded(transfer_encoding = nil, charset = nil)
# PATCH
# Use provided parameter charset (from parent Message) if not nil,
# otherwise use own self.charset
# Required because the Message potentially has on its headers the charset
# that needs to be used (e.g. 'Content-Type: text/plain; charset=UTF-8')
charset = self.charset if charset.nil?
if multipart?
self.sort_parts!
encoded_parts = parts.map { |p| p.encoded }
([preamble] + encoded_parts).join(crlf_boundary) + end_boundary + epilogue.to_s
else
dec = Mail::Encodings.get_encoding(encoding)
enc = if Utilities.blank?(transfer_encoding)
dec
else
negotiate_best_encoding(transfer_encoding)
end
if dec.nil?
# Cannot decode, so skip normalization
raw_source
else
# Decode then encode to normalize and allow transforming
# from base64 to Q-P and vice versa
decoded = dec.decode(raw_source)
if defined?(Encoding) && charset && charset != "US-ASCII"
# PATCH
# We need to force the encoding: in the case of quoted-printable
# this will throw an exception otherwise, because `decoded` will have
# an encoding of BINARY (or its equivalent ASCII-8BIT),
# coming from QuotedPrintable#decode, and inside it from String#unpack1
decoded = decoded.force_encoding(charset)
decoded.force_encoding('BINARY') unless Encoding.find(charset).ascii_compatible?
end
enc.encode(decoded)
end
end
end
end
class Message
def encoded
ready_to_send!
buffer = header.encoded
buffer << "\r\n"
# PATCH
# Pass the Message charset down to the contained Body, the headers
# potentially contain the charset needed to be applied
buffer << body.encoded(content_transfer_encoding, charset)
buffer
end
end
end