-------------------------------------------------------------------- -- | -- Module : Codec.MIME.QuotedPrintable -- Copyright : (c) 2006-2009, Galois, Inc. -- License : BSD3 -- -- Maintainer: Sigbjorn Finne <sigbjorn.finne@gmail.com> -- Stability : provisional -- Portability: -- -- To and from QP content encoding. -- -------------------------------------------------------------------- module Codec.MIME.QuotedPrintable ( decode -- :: String -> String , encode -- :: String -> String ) where import Data.Char -- | 'decode' incoming quoted-printable content, stripping -- out soft line breaks and translating @=XY@ sequences -- into their decoded byte\/octet. The output encoding\/representation -- is still a String, not a sequence of bytes. decode :: String -> String decode "" = "" decode ('=':'\r':'\n':xs) = decode xs -- soft line break. decode ('=':x1:x2:xs) | isHexDigit x1 && isHexDigit x2 = chr (digitToInt x1 * 16 + digitToInt x2) : decode xs decode ('=':xs) = '=':decode xs -- make it explicit that we propagate other '=' occurrences. decode (x1:xs) = x1:decode xs -- | 'encode' converts a sequence of characeter _octets_ into -- quoted-printable form; suitable for transmission in MIME -- payloads. Note the stress on _octets_; it is assumed that -- you have already converted Unicode into a <=8-bit encoding -- (UTF-8, most likely.) encode :: String -> String encode xs = encodeLength 0 xs -- | @encodeLength llen str@ is the worker function during encoding. -- The extra argument @llen@ tracks the current column for the line -- being processed. Soft line breaks are inserted if a line exceeds -- a max length. encodeLength :: Int -> String -> String encodeLength _ "" = "" encodeLength n (x:xs) | n >= 72 = '=':'\r':'\n':encodeLength 0 (x:xs) encodeLength _ ('=':xs) = '=':'3':'D':encodeLength 0 xs encodeLength n (x:xs) | ox >= 0x100 = error ("QuotedPrintable.encode: encountered > 8 bit character: " ++ show (x,ox)) | n >= 72 = '=':'\r':'\n':encodeLength 0 (x:xs) | ox >= 0x21 && ox <= 0x7e = x : encodeLength (n+1) xs | ox == 0x09 || ox == 0x20 = x : encodeLength (n+1) xs | otherwise = '=':showH (ox `div` 0x10): showH (ox `mod` 0x10):encodeLength (n+3) xs where ox = ord x showH v | v < 10 = chr (ord_0 + v) | otherwise = chr (ord_A + (v-10)) ord_0 = ord '0' ord_A = ord 'A'