#
# $Id: base64handler.icn,v 1.1 2004/02/12 17:07:55 rparlett Exp $
#

package mail

global base64_string, base64_cset

#
# Handles the base64 encoding of a message
#
class Base64Handler:EncodingHandler()
   method can_handle(enc)
      return map(enc) == "base64"
   end

   method decode_data(m, data)
      local s, t, w, x, y, z, res

      static n64
      initial {
         n64 := string(&cset)[1+:64]
      }

      # Transform the data by stripping out all non base64 encoding chars.
      t := ""
      pad := 0
      data ? {
         while tab(upto(base64_cset)) do
            t ||:= tab(many(base64_cset))
         while tab(upto('=')) do {
            s := tab(many('='))
            pad +:= *s
            t ||:= repl("\x00", *s)
         }
      }

      if (pad > 2) | (*t % 4 ~= 0) then 
         return error("Badly formatted Base64 data")

      t := map(t, base64_string, n64)

      res := ""
      t ? while ( w := ord(move(1)), x := ord(move(1)), y := ord(move(1)), z := ord(move(1)) ) do {
         res ||:= char( ior( ishift(w,2), ishift(x,-4) ) )
         res ||:= char( ior( iand(ishift(x,4),255), ishift(y,-2) ) )
         res ||:= char( ior( iand(ishift(y,6),255), z ) )
      }

      res[0 -: pad] := ""

      return res
   end

   method encode_data(m, data) 
      local i, j, k, pad, res, ll

      res := ""
      ll := 0

      pad := (3 - (*data % 3)) % 3

      data ||:= repl("\x00", pad)

      data ? while (i := ord(move(1)), j := ord(move(1)), k := ord(move(1))) do {
         res ||:= base64_string[ 1 + ishift(i,-2) ]
         res ||:= base64_string[ 1 + ior( ishift(iand(i,3),4), ishift(j,-4) ) ]
         res ||:= base64_string[ 1 + ior( ishift(iand(j,15),2), ishift(k,-6) ) ]
         res ||:= base64_string[ 1 + iand(k,63) ]
         ll +:= 1
         if ll = 19 then {
            res ||:= "\r\n"
            ll := 0
         }
      }
      res[ 0 -: pad ] := repl("=", pad) || "\r\n"

      return res
   end

   initially()
      initial {
         base64_string := &ucase || &lcase || &digits || "+/"
         base64_cset := cset(base64_string)
      }
end