base64_decode.ls

on base64_decode encodedStr
  --  
  -- Base 64 encoding for director strings.
  -- http://www.fourmilab.ch/webtools/base64/rfc1341.html
  --
  -- Cole Tierney -- <colet at putnamhill dot net>
  --
  --
  --  First setup some constants to make our life easier.
  --
  --  set bits12Mask = integer( 255 + 15 * power(2,8) )                      --==     4095
  set bits12Mask = 4095               -- 00000000 00000000 00001111 11111111
  set bit13Mask = bits12Mask + 1      -- 00000000 00000000 00010000 00000000
  --
  --  set bits18Mask = integer( 255 + 255 * power(2,8) + 3 * power(2,16) )   --==   262143
  set bits18Mask = 262143             -- 00000000 00000011 11111111 11111111
  set bit19Mask = bits18Mask + 1      -- 00000000 00000100 00000000 00000000
  --
  --  set bits24Mask = integer( 255 + 255 * power(2,8) + 255 * power(2,16) ) --== 16777215
  set bits24Mask = 16777215           -- 00000000 11111111 11111111 11111111
  set bit25Mask = bits24Mask + 1      -- 00000001 00000000 00000000 00000000
  --
  set base64alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
  --
  -- Since lingo is case insensitive, we'll translate the above into their ascii equivalents.
  set base64values = []
  set len = length(base64alphabet)
  repeat with i = 1 to len
    add base64values, chartonum( char i of base64alphabet )
  end repeat
  --
  set base64alphabet = "" -- Don't need this anymore.
  --
  --
  
  
  set decodedStr = ""
  
  set bit24grp = ""
  
  repeat while length( encodedStr ) > 0
    
    -- Get next character from the string we passed.
    set ch = char 1 of encodedStr
    delete char 1 of encodedStr
    
    -- Try to add it to our group of 4 valid base 64 characters.
    if length( bit24grp ) < 4 then
      set asciiNum = charToNum(ch)
      if getPos( base64values, asciiNum ) > 0 or ch = "=" then -- This is a base 64 character, so collect it.
        put ch after bit24grp
      end if
    end if    
    
    
    if length( bit24grp ) = 4 then -- We have a commplete group of 4 base64 characters.  
      
      -- If we've got a complete group then decode it...   
      set padCharOffset = offset( "=", bit24grp )
      
      if padCharOffset = 0 then -- Process a normal unpadded group.
        
        -- we can assume that there are no special processing characters ("=").
        -- If here, then we have a complete set of 3 bytes.
        set b64int = 0
        repeat while length( bit24grp ) > 0
          
          set b64ch = char 1 of bit24grp
          delete char 1 of bit24grp
          
          set asciiNum = chartonum( b64ch )
          set val = getPos( base64values, asciiNum ) - 1
          set b64int = b64int * 64 -- Shift left 6 bits (double it 6 times or multiply by 64).
          set b64int = bitOr( b64int, val )
          
        end repeat
        
        -- Ok, now extract the 3 8 bit bytes from our integer (all 24 bits are significant).
        repeat with i = 1 to 3
          
          set bit8int = 0
          repeat with j = 1 to 8
            set bit8int = bit8int * 2 -- Make room for more bits.
            set b64int = b64int * 2
            set bit8int = bitOr( bit8int, bitAnd( b64int, bit25Mask ) > 0 ) -- ...roll this bit into our 8 bit number.
            set b64int = bitAnd( b64int, bits24Mask ) -- Strip the bit from our 24 bit integer and...
          end repeat
          
          -- Should have our 8 bit int, so turn it into a char so we can add it to our decoded str.
          put numToChar( bit8int ) after decodedStr
        end repeat
        
      else
        
        -- Else we must have our last group that happens to be padded with the "=" character.     
        -- The masks we use will be determined by how many bits we have to processed, so set that first.
        if padCharOffset = 3 then -- The offset of the pad character must be either 3 or 4.
          maskbits = bits12Mask -- Only two 6bit characters, so use a 12 bit mask.
          highBitMask = bit13Mask  
        else -- must be 18 bits
          maskBits = bits18Mask
          highBitMask = bit19Mask  
        end if
        
        set bit24grp = char 1 to padCharOffset - 1 of bit24grp -- First strip off the pad charaters.
        set b64int = 0
        repeat while length( bit24grp ) > 0
          
          set b64ch = char 1 of bit24grp
          delete char 1 of bit24grp
          
          set asciiNum = chartonum( b64ch )
          set val = getPos( base64values, asciiNum ) - 1
          set b64int = b64int * 64 -- Shift left 6 bits (double it 6 times or multiply by 64).
          set b64int = bitOr( b64int, val )
          
        end repeat
        -- Ok, now extract the bits from the integer.
        
        repeat while b64int > 0 -- As long there are bits left in our integer, keep rotating out 8 bit charaters.
          
          set bit8int = 0
          repeat with j = 1 to 8
            set bit8int = bit8int * 2 -- Make room for more bits.
            set b64int = b64int * 2 -- Shift bits to left once.
            set bit8int = bitOr( bit8int, bitAnd( b64int, highBitMask ) > 0 ) -- ...roll this bit into our 8 bit number.
            set b64int = bitAnd( b64int, maskBits ) -- Strip the bit from our 18 bit integer and...
          end repeat
          
          -- Should have our 8 bit int, so turn it into a char so we can add it to our decoded str.
          put numToChar( bit8int ) after decodedStr
          
        end repeat
        
      end if -- else finished decoding last group containing special processing character ("=").
      
    end if -- Have a base64 group
    
  end repeat -- End of processing base64 characters.
  
  
  return decodedStr  
  
end