PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/bundle/gems/mail-2.5.4/lib/mail/encodings.rb

https://github.com/bahakz/first_app
Ruby | 305 lines | 174 code | 32 blank | 99 comment | 27 complexity | 5a3aef0ef5e58379e76f3c2865641ded MD5 | raw file
Possible License(s): Apache-2.0, MIT, GPL-2.0, BSD-3-Clause
  1. # encoding: utf-8
  2. module Mail
  3. # Raised when attempting to decode an unknown encoding type
  4. class UnknownEncodingType < StandardError #:nodoc:
  5. end
  6. module Encodings
  7. include Mail::Patterns
  8. extend Mail::Utilities
  9. @transfer_encodings = {}
  10. # Register transfer encoding
  11. #
  12. # Example
  13. #
  14. # Encodings.register "base64", Mail::Encodings::Base64
  15. def Encodings.register(name, cls)
  16. @transfer_encodings[get_name(name)] = cls
  17. end
  18. # Is the encoding we want defined?
  19. #
  20. # Example:
  21. #
  22. # Encodings.defined?(:base64) #=> true
  23. def Encodings.defined?( str )
  24. @transfer_encodings.include? get_name(str)
  25. end
  26. # Gets a defined encoding type, QuotedPrintable or Base64 for now.
  27. #
  28. # Each encoding needs to be defined as a Mail::Encodings::ClassName for
  29. # this to work, allows us to add other encodings in the future.
  30. #
  31. # Example:
  32. #
  33. # Encodings.get_encoding(:base64) #=> Mail::Encodings::Base64
  34. def Encodings.get_encoding( str )
  35. @transfer_encodings[get_name(str)]
  36. end
  37. def Encodings.get_all
  38. @transfer_encodings.values
  39. end
  40. def Encodings.get_name(enc)
  41. enc = enc.to_s.gsub("-", "_").downcase
  42. end
  43. # Encodes a parameter value using URI Escaping, note the language field 'en' can
  44. # be set using Mail::Configuration, like so:
  45. #
  46. # Mail.defaults do
  47. # param_encode_language 'jp'
  48. # end
  49. #
  50. # The character set used for encoding will either be the value of $KCODE for
  51. # Ruby < 1.9 or the encoding on the string passed in.
  52. #
  53. # Example:
  54. #
  55. # Mail::Encodings.param_encode("This is fun") #=> "us-ascii'en'This%20is%20fun"
  56. def Encodings.param_encode(str)
  57. case
  58. when str.ascii_only? && str =~ TOKEN_UNSAFE
  59. %Q{"#{str}"}
  60. when str.ascii_only?
  61. str
  62. else
  63. RubyVer.param_encode(str)
  64. end
  65. end
  66. # Decodes a parameter value using URI Escaping.
  67. #
  68. # Example:
  69. #
  70. # Mail::Encodings.param_decode("This%20is%20fun", 'us-ascii') #=> "This is fun"
  71. #
  72. # str = Mail::Encodings.param_decode("This%20is%20fun", 'iso-8559-1')
  73. # str.encoding #=> 'ISO-8859-1' ## Only on Ruby 1.9
  74. # str #=> "This is fun"
  75. def Encodings.param_decode(str, encoding)
  76. RubyVer.param_decode(str, encoding)
  77. end
  78. # Decodes or encodes a string as needed for either Base64 or QP encoding types in
  79. # the =?<encoding>?[QB]?<string>?=" format.
  80. #
  81. # The output type needs to be :decode to decode the input string or :encode to
  82. # encode the input string. The character set used for encoding will either be
  83. # the value of $KCODE for Ruby < 1.9 or the encoding on the string passed in.
  84. #
  85. # On encoding, will only send out Base64 encoded strings.
  86. def Encodings.decode_encode(str, output_type)
  87. case
  88. when output_type == :decode
  89. Encodings.value_decode(str)
  90. else
  91. if str.ascii_only?
  92. str
  93. else
  94. Encodings.b_value_encode(str, find_encoding(str))
  95. end
  96. end
  97. end
  98. # Decodes a given string as Base64 or Quoted Printable, depending on what
  99. # type it is.
  100. #
  101. # String has to be of the format =?<encoding>?[QB]?<string>?=
  102. def Encodings.value_decode(str)
  103. # Optimization: If there's no encoded-words in the string, just return it
  104. return str unless str =~ /\=\?[^?]+\?[QB]\?[^?]+?\?\=/xmi
  105. lines = collapse_adjacent_encodings(str)
  106. # Split on white-space boundaries with capture, so we capture the white-space as well
  107. lines.map do |line|
  108. line.split(/([ \t])/).map do |text|
  109. if text.index('=?').nil?
  110. text
  111. else
  112. # Search for occurences of quoted strings or plain strings
  113. text.scan(/( # Group around entire regex to include it in matches
  114. \=\?[^?]+\?([QB])\?[^?]+?\?\= # Quoted String with subgroup for encoding method
  115. | # or
  116. .+?(?=\=\?|$) # Plain String
  117. )/xmi).map do |matches|
  118. string, method = *matches
  119. if method == 'b' || method == 'B'
  120. b_value_decode(string)
  121. elsif method == 'q' || method == 'Q'
  122. q_value_decode(string)
  123. else
  124. string
  125. end
  126. end
  127. end
  128. end
  129. end.flatten.join("")
  130. end
  131. # Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
  132. def Encodings.unquote_and_convert_to(str, to_encoding)
  133. output = value_decode( str ).to_s # output is already converted to UTF-8
  134. if 'utf8' == to_encoding.to_s.downcase.gsub("-", "")
  135. output
  136. elsif to_encoding
  137. begin
  138. if RUBY_VERSION >= '1.9'
  139. output.encode(to_encoding)
  140. else
  141. require 'iconv'
  142. Iconv.iconv(to_encoding, 'UTF-8', output).first
  143. end
  144. rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
  145. # the 'from' parameter specifies a charset other than what the text
  146. # actually is...not much we can do in this case but just return the
  147. # unconverted text.
  148. #
  149. # Ditto if either parameter represents an unknown charset, like
  150. # X-UNKNOWN.
  151. output
  152. end
  153. else
  154. output
  155. end
  156. end
  157. def Encodings.address_encode(address, charset = 'utf-8')
  158. if address.is_a?(Array)
  159. # loop back through for each element
  160. address.compact.map { |a| Encodings.address_encode(a, charset) }.join(", ")
  161. else
  162. # find any word boundary that is not ascii and encode it
  163. encode_non_usascii(address, charset) if address
  164. end
  165. end
  166. def Encodings.encode_non_usascii(address, charset)
  167. return address if address.ascii_only? or charset.nil?
  168. us_ascii = %Q{\x00-\x7f}
  169. # Encode any non usascii strings embedded inside of quotes
  170. address = address.gsub(/(".*?[^#{us_ascii}].*?")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
  171. # Then loop through all remaining items and encode as needed
  172. tokens = address.split(/\s/)
  173. map_with_index(tokens) do |word, i|
  174. if word.ascii_only?
  175. word
  176. else
  177. previous_non_ascii = i>0 && tokens[i-1] && !tokens[i-1].ascii_only?
  178. if previous_non_ascii #why are we adding an extra space here?
  179. word = " #{word}"
  180. end
  181. Encodings.b_value_encode(word, charset)
  182. end
  183. end.join(' ')
  184. end
  185. # Encode a string with Base64 Encoding and returns it ready to be inserted
  186. # as a value for a field, that is, in the =?<charset>?B?<string>?= format
  187. #
  188. # Example:
  189. #
  190. # Encodings.b_value_encode('This is あ string', 'UTF-8')
  191. # #=> "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?="
  192. def Encodings.b_value_encode(encoded_str, encoding = nil)
  193. return encoded_str if encoded_str.to_s.ascii_only?
  194. string, encoding = RubyVer.b_value_encode(encoded_str, encoding)
  195. map_lines(string) do |str|
  196. "=?#{encoding}?B?#{str.chomp}?="
  197. end.join(" ")
  198. end
  199. # Encode a string with Quoted-Printable Encoding and returns it ready to be inserted
  200. # as a value for a field, that is, in the =?<charset>?Q?<string>?= format
  201. #
  202. # Example:
  203. #
  204. # Encodings.q_value_encode('This is あ string', 'UTF-8')
  205. # #=> "=?UTF-8?Q?This_is_=E3=81=82_string?="
  206. def Encodings.q_value_encode(encoded_str, encoding = nil)
  207. return encoded_str if encoded_str.to_s.ascii_only?
  208. string, encoding = RubyVer.q_value_encode(encoded_str, encoding)
  209. string.gsub!("=\r\n", '') # We already have limited the string to the length we want
  210. map_lines(string) do |str|
  211. "=?#{encoding}?Q?#{str.chomp.gsub(/ /, '_')}?="
  212. end.join(" ")
  213. end
  214. private
  215. # Decodes a Base64 string from the "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=" format
  216. #
  217. # Example:
  218. #
  219. # Encodings.b_value_decode("=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=")
  220. # #=> 'This is あ string'
  221. def Encodings.b_value_decode(str)
  222. RubyVer.b_value_decode(str)
  223. end
  224. # Decodes a Quoted-Printable string from the "=?UTF-8?Q?This_is_=E3=81=82_string?=" format
  225. #
  226. # Example:
  227. #
  228. # Encodings.q_value_decode("=?UTF-8?Q?This_is_=E3=81=82_string?=")
  229. # #=> 'This is あ string'
  230. def Encodings.q_value_decode(str)
  231. RubyVer.q_value_decode(str)
  232. end
  233. def Encodings.split_encoding_from_string( str )
  234. match = str.match(/\=\?([^?]+)?\?[QB]\?(.+)?\?\=/mi)
  235. if match
  236. match[1]
  237. else
  238. nil
  239. end
  240. end
  241. def Encodings.find_encoding(str)
  242. RUBY_VERSION >= '1.9' ? str.encoding : $KCODE
  243. end
  244. # Gets the encoding type (Q or B) from the string.
  245. def Encodings.split_value_encoding_from_string(str)
  246. match = str.match(/\=\?[^?]+?\?([QB])\?(.+)?\?\=/mi)
  247. if match
  248. match[1]
  249. else
  250. nil
  251. end
  252. end
  253. # When the encoded string consists of multiple lines, lines with the same
  254. # encoding (Q or B) can be joined together.
  255. #
  256. # String has to be of the format =?<encoding>?[QB]?<string>?=
  257. def Encodings.collapse_adjacent_encodings(str)
  258. lines = str.split(/(\?=)\s*(=\?)/).each_slice(2).map(&:join)
  259. results = []
  260. previous_encoding = nil
  261. lines.each do |line|
  262. encoding = split_value_encoding_from_string(line)
  263. if encoding == previous_encoding
  264. line = results.pop + line
  265. line.gsub!(/\?\=\=\?.+?\?[QqBb]\?/m, '')
  266. end
  267. previous_encoding = encoding
  268. results << line
  269. end
  270. results
  271. end
  272. end
  273. end