]> git.cworth.org Git - sup/blob - lib/sup/rfc2047.rb
easy_decode now also catches InvalidCharacter
[sup] / lib / sup / rfc2047.rb
1 ## from: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/101949
2
3 # $Id: rfc2047.rb,v 1.4 2003/04/18 20:55:56 sam Exp $
4 # MODIFIED slightly by William Morgan
5 #
6 # An implementation of RFC 2047 decoding.
7 #
8 # This module depends on the iconv library by Nobuyoshi Nakada, which I've 
9 # heard may be distributed as a standard part of Ruby 1.8. Many thanks to him
10 # for helping with building and using iconv.
11 #
12 # Thanks to "Josef 'Jupp' Schugt" <jupp / gmx.de> for pointing out an error with
13 # stateful character sets.
14 #
15 # Copyright (c) Sam Roberts <sroberts / uniserve.com> 2004
16 #
17 # This file is distributed under the same terms as Ruby.
18
19 require 'iconv'
20
21 module Rfc2047
22   WORD = %r{=\?([!\#$%&'*+-/0-9A-Z\\^\`a-z{|}~]+)\?([BbQq])\?([!->@-~]+)\?=} # :nodoc: 'stupid ruby-mode
23   WORDSEQ = %r{(#{WORD.source})\s+(?=#{WORD.source})}
24
25   def Rfc2047.is_encoded? s; s =~ WORD end
26
27   # Decodes a string, +from+, containing RFC 2047 encoded words into a target
28   # character set, +target+. See iconv_open(3) for information on the
29   # supported target encodings. If one of the encoded words cannot be
30   # converted to the target encoding, it is left in its encoded form.
31   def Rfc2047.decode_to(target, from)
32     from = from.gsub(WORDSEQ, '\1')
33     out = from.gsub(WORD) do
34       |word|
35       charset, encoding, text = $1, $2, $3
36
37       # B64 or QP decode, as necessary:
38       case encoding
39         when 'b', 'B'
40           #puts text
41           text = text.unpack('m*')[0]
42           #puts text.dump
43
44         when 'q', 'Q'
45           # RFC 2047 has a variant of quoted printable where a ' ' character
46           # can be represented as an '_', rather than =32, so convert
47           # any of these that we find before doing the QP decoding.
48           text = text.tr("_", " ")
49           text = text.unpack('M*')[0]
50
51         # Don't need an else, because no other values can be matched in a
52         # WORD.
53       end
54
55       Iconv.easy_decode(target, charset, text)
56     end
57   end
58 end