1. --****************************************************************************
  2. -- Script:   BASE64 CLASS
  3. -- Version:  1.3
  4. -- Date:     2015-02-03
  5. -- Author:   Valentin Schmidt
  6. --
  7. -- Requirements/Dependencies:
  8. -- o parent script "ProgressDialog" (optional)
  9. --
  10. --****************************************************************************
  11.  
  12. property pBase64Lookup
  13. property pSubLengthLim
  14. property pReturn
  15. property pMimeHeader
  16. property pIsMac
  17.  
  18. ----------------------------------------
  19. -- @constructor
  20. ----------------------------------------
  21. on new me
  22.   pBase64Lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
  23.   pIsMac = the platform contains "mac"
  24.  
  25.   -- defaults
  26.   pSubLengthLim = 6000 -- tune for optimization, win: 1000 ? mac: 6000 ?
  27.   pReturn = numtochar(13)&numtochar(10)
  28.   pMimeHeader = ""
  29.  
  30.   return me
  31. end
  32.  
  33. ----------------------------------------
  34. -- HOOK FOR PROGRESS BAR (overwrite, either directly or in child script!)
  35. -- @param {float} prog
  36. ----------------------------------------
  37. on showProgress (me, prog)
  38.   -- put prog*100 && "%"
  39. end
  40.  
  41. ----------------------------------------
  42. -- Sets mime header
  43. -- If called without arguments, header is reset to empty (=no header)
  44. -- @param {string} tFilename - optional
  45. -- @param {string} tMimeType - optional
  46. ----------------------------------------
  47. on setMimeHeader (me, tFilename, tMimeType)
  48.   pMimeHeader = ""
  49.   if stringP(tFilename) then
  50.     if stringP(tMimeType) then put "Content-Type: "&tMimeType&";"&pReturn after pMimeHeader
  51.     put "Content-Transfer-Encoding: base64"&pReturn after pMimeHeader
  52.     put "Content-Disposition: inline; filename="&QUOTE&tFilename&QUOTE&pReturn&pReturn after pMimeHeader
  53.   end if
  54. end
  55.  
  56. ----------------------------------------
  57. -- Encodes binary string
  58. -- @param {string} str
  59. -- @param {bool} splitChunks - optional
  60. -- @return {string}
  61. ----------------------------------------
  62. on base64Encode (me, str, splitChunks)
  63.   if pIsMac then
  64.     return me.base64EncodeMAC (str, splitChunks)
  65.   else
  66.     return me.base64EncodePC (str, splitChunks)
  67.   end if
  68. end
  69.  
  70. ----------------------------------------
  71. -- Encodes binary string (PC version)
  72. -- Optimized for PC by LingoGuy@sbcglobal.net   3/29/04
  73. -- @param {string} str
  74. -- @param {bool} splitChunks - optional
  75. -- @return {string}
  76. ----------------------------------------
  77. on base64EncodePC (me, str, splitChunks)
  78.   subStrings = []
  79.   out = ""
  80.   i=1
  81.   strRemainder = str.length mod 3
  82.   strLength = str.length - strRemainder
  83.  
  84.   repeat while i < strLength
  85.    
  86.     if i mod 100 = 0 then me.showProgress(float(i)/strLength)
  87.    
  88.     a = charToNum( str.char[ i ] )
  89.     b = charToNum( str.char[ i+1 ] )
  90.     c = charToNum( str.char[ i+2 ] )
  91.     put pBase64Lookup.char[ ( bitAnd( a, 252 ) / 4 ) + 1 ] after out
  92.     put pBase64Lookup.char[ ( bitAnd( (a*256)+b, 1008 ) / 16 ) + 1 ] after out
  93.     put pBase64Lookup.char[ ( bitAnd( (b*256)+c, 4032 ) / 64 ) + 1 ] after out
  94.     put pBase64Lookup.char[ ( bitAnd( c, 63 ) ) + 1] after out
  95.     i = i + 3
  96.     if splitChunks AND i mod 54=1 then put pReturn after out
  97.     if length(out) > pSubLengthLim then --- cach segment in array, start new out string
  98.       subStrings.append(out)
  99.       out = empty
  100.     end if
  101.   end repeat
  102.   if (strRemainder = 1) then
  103.     a = charToNum( str.char[ i ] )
  104.     put pBase64Lookup.char[ ( bitAnd( a, 252 ) / 4 ) + 1 ] after out
  105.     put pBase64Lookup.char[ ( bitAnd( a*256, 1008 ) / 16 ) + 1 ] after out
  106.     put "=" after out
  107.     put "=" after out
  108.   else if (strRemainder = 2) then
  109.     a = charToNum( str.char[ i ] )
  110.     b = charToNum( str.char[ i+1 ] )
  111.     put pBase64Lookup.char[ ( bitAnd( a, 252 ) / 4 ) + 1 ] after out
  112.     put pBase64Lookup.char[ ( bitAnd( (a*256)+b, 1008 ) / 16 ) + 1 ] after out
  113.     put pBase64Lookup.char[ ( bitAnd( b*256, 4032 ) / 64 ) + 1 ] after out
  114.     put "=" after out      
  115.   end if
  116.  
  117.   b64Str = ""
  118.   repeat with s in subStrings
  119.     put s after b64Str
  120.   end repeat
  121.   put out after b64Str -- last uncached bit
  122.  
  123.   if splitChunks then
  124.     if b64Str.char[b64Str.length-pReturn.length+1..b64Str.length]=pReturn then
  125.       delete char (b64Str.length-pReturn.length+1) to b64Str.length of b64Str
  126.     end if
  127.   end if
  128.  
  129.   put pMimeHeader before b64Str
  130.  
  131.   me.showProgress(1)
  132.  
  133.   return b64Str
  134. end
  135.  
  136. ----------------------------------------
  137. -- Encodes binary string (Mac version)
  138. -- Optimized for mac by Alex da Franca
  139. -- @param {string} str
  140. -- @param {bool} splitChunks - optional
  141. -- @return {string}
  142. ----------------------------------------
  143. on base64EncodeMAC (me, str, splitChunks)
  144.  
  145.   out = ""
  146.   i=1
  147.  
  148.   strLength = str.length
  149.   strRemainder = strLength mod 3
  150.   strLength = strLength - strRemainder
  151.  
  152.   chunk = char 1 to pSubLengthLim of str
  153.   delete char 1 to pSubLengthLim of str
  154.  
  155.   b64Str = ""
  156.  
  157.   repeat while i < strLength
  158.    
  159.     if i mod 100 = 0 then me.showProgress(float(i)/strLength)
  160.    
  161.     a = charToNum( chunk.char[ 1 ] )
  162.     b = charToNum( chunk.char[ 2 ] )
  163.     c = charToNum( chunk.char[ 3 ] )
  164.    
  165.     put pBase64Lookup.char[ ( bitAnd( a, 252 ) / 4 ) + 1 ] after out
  166.     put pBase64Lookup.char[ ( bitAnd( (a*256)+b, 1008 ) / 16 ) + 1 ] after out
  167.     put pBase64Lookup.char[ ( bitAnd( (b*256)+c, 4032 ) / 64 ) + 1 ] after out
  168.     put pBase64Lookup.char[ ( bitAnd( c, 63 ) ) + 1] after out
  169.    
  170.     i = i + 3
  171.     if splitChunks AND i mod 54=1 then put pReturn after out
  172.    
  173.     delete char 1 to 3 of chunk
  174.    
  175.     if (i - 1) mod pSubLengthLim = 0 then
  176.       chunk = char 1 to pSubLengthLim of str
  177.       delete char 1 to pSubLengthLim of str
  178.       put out after b64Str
  179.       out = ""
  180.     end if
  181.    
  182.   end repeat
  183.  
  184.   if( strRemainder = 1 ) then
  185.    
  186.     a = charToNum( chunk.char[ 1 ] )
  187.    
  188.     put pBase64Lookup.char[ ( bitAnd( a, 252 ) / 4 ) + 1 ] after out
  189.     put pBase64Lookup.char[ ( bitAnd( a*256, 1008 ) / 16 ) + 1 ] after out
  190.     put "==" after out
  191.    
  192.   else if ( strRemainder = 2 ) then
  193.    
  194.     a = charToNum( chunk.char[ 1 ] )
  195.     b = charToNum( chunk.char[ 2 ] )
  196.    
  197.     put pBase64Lookup.char[ ( bitAnd( a, 252 ) / 4 ) + 1 ] after out
  198.     put pBase64Lookup.char[ ( bitAnd( (a*256)+b, 1008 ) / 16 ) + 1 ] after out
  199.     put pBase64Lookup.char[ ( bitAnd( b*256, 4032 ) / 64 ) + 1 ] after out
  200.     put "=" after out
  201.    
  202.   end if
  203.  
  204.   put out after b64Str -- last uncached bit
  205.  
  206.   if splitChunks then
  207.     if b64Str.char[b64Str.length-pReturn.length+1..b64Str.length]=pReturn then
  208.       delete char (b64Str.length-pReturn.length+1) to b64Str.length of b64Str
  209.     end if
  210.   end if
  211.  
  212.   put pMimeHeader before b64Str
  213.  
  214.   me.showProgress(1)
  215.  
  216.   return b64Str
  217. end
  218.  
  219. ----------------------------------------
  220. -- Decodes Base64 encoded string, returns (binary) string or false
  221. -- @param {string} str
  222. -- @return {string|false}
  223. ----------------------------------------
  224. on base64Decode (me, str)
  225.   Base64Lookup = [ "A":0, "B":1, "C":2, "D":3, "E":4, "F":5, "G":6, "H":7, "I":8, \
  226. "J":9, "K":10, "L":11, "M":12, "N":13, "O":14, "P":15, "Q":16, "R":17, "S":18, \
  227. "T":19, "U":20, "V":21, "W":22, "X":23, "Y":24, "Z":25, "a":26, "b":27, "c":28, \
  228. "d":29, "e":30, "f":31, "g":32, "h":33, "i":34, "j":35,  "k":36, "l":37, "m":38, \
  229. "n":39, "o":40, "p":41, "q":42, "r":43, "s":44, "t":45, "u":46, "v":47, "w":48, \
  230. "x":49, "y":50, "z":51, "0":52, "1":53, "2":54, "3":55, "4":56, "5":57, "6":58, \
  231. "7":59, "8":60, "9":61, "+":62, "/":63 ]
  232.  
  233.   n = 1
  234.  
  235.   -- mime header detection:
  236.   if str.line[1] contains ":" then
  237.     check = [numtochar(13)&numtochar(10)&numtochar(13)&numtochar(10), numtochar(13)&numtochar(13), numtochar(10)&numtochar(10)]
  238.     repeat with c in check
  239.       n = offset(c, str)
  240.       if n > 0 then
  241.         n = n+c.length
  242.         exit repeat
  243.       end if
  244.     end repeat
  245.     if n=0 then
  246.       put "error parsing mime header!"
  247.       return false
  248.     end if
  249.   end if
  250.  
  251.   subStrings = []
  252.   out = ""
  253.  
  254.   quanta = []
  255.   repeat with i = n to str.length
  256.     c = Base64Lookup.getaProp( str.char[i] )
  257.     if not voidP(c) then quanta.add(c)
  258.     if( quanta.count = 4 ) then
  259.       put numToChar( bitAnd( (quanta[1]*64)+quanta[2], 4080 ) / 16 ) after out
  260.       put numToChar( bitAnd( (quanta[2]*64)+quanta[3], 1020 ) / 4 ) after out
  261.       put numToChar( bitAnd( (quanta[3]*64)+quanta[4], 255 ) ) after out
  262.       quanta = []
  263.      
  264.       if length(out) > pSubLengthLim then --- cache segment in list, start new out string
  265.         subStrings.append(out)
  266.         out = empty
  267.       end if
  268.      
  269.     end if
  270.   end repeat
  271.  
  272.   if( quanta.count = 3 ) then
  273.     put numToChar( bitAnd( (quanta[1]*64)+quanta[2], 4080 ) / 16 ) after out
  274.     put numToChar( bitAnd( (quanta[2]*64)+quanta[3], 1020 ) / 4 ) after out
  275.   else if( quanta.count = 2 ) then
  276.     put numToChar( bitAnd( (quanta[1]*64)+quanta[2], 4080 ) / 16 ) after out
  277.   end if
  278.  
  279.   str = ""
  280.   repeat with s in subStrings
  281.     put s after str
  282.   end repeat
  283.   put out after str -- last uncached bit
  284.  
  285.   return str
  286. end
  287.  
[raw code]