Edit: Added of_decode_base64_to_blob at bottom.
There is a PB example out there for Base64 encoding and decoding, but it relies on calling COM objects. (At least the few I found did..) i.e :
string ls_ret
oleobject lo_xml
oleobject lo_node
lo_xml = create oleobject
lo_xml.connecttonewobject("Microsoft.XMLDOM")
lo_node = lo_xml.createElement("b64")
lo_node.dataType = "bin.base64"
//Encode
lo_node.NodeTypedValue = Blob(as_data)
ls_ret = lo_node.Text
//Decode
//lo_node.Text = as_data
//ls_ret = String( lo_node.NodeTypedValue )
Destroy lo_node
Destroy lo_xml
return ls_ret
Well, I came across a situation where I needed to call PB functions from a PBNI interface. One of the functions called used the above function to decode base64. Well for a reason I've yet to figure out, calling PB from PBNI fails on "create oleobject". Fail. So I rewrote the base64 decoding and encoding to do it manually and remove the dependency of the oleobject. The math could probably be simplified in here..but im too lazy to think.
Note: Both only work with unicode strings (Default for PB strings)
Note2: I used code found here
http://www.motobit.com/tips/detpg_Base64/ as a base (youll notice more in the decode section as I got lazier and didnt rename the variables..)
Encode:
public function string of_encode_base64 (string as_data);
string ls_data, ls_ret
string ls_base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
string ls_tmp
longlong ll_24bits, ll_tmp, ll_tmp2, ll_tmp3
longlong ll_1, ll_2, ll_3, ll_4, ll_i
long ll_newline_offset
ls_data = as_data
ls_ret = ""
SetNull( ll_tmp3 )
For ll_i = 1 To longlong( Len( ls_data ) + 1 )
//Create 24bit buffer from unicode
If IsNull( ll_tmp3 ) Then
//Reverse first two bytes
ll_tmp = Asc( Mid( ls_data, ll_i, 1 ) )
ll_tmp = (( ll_tmp - ( Int( ll_tmp / 256 ) * 256 ) ) * 256) + ( ll_tmp / 256 )
ll_i++
//Get 3rd byte and store 4th for later
ll_tmp2 = Asc( Mid( ls_data, ll_i, 1) )
ll_tmp3 = Int( ll_tmp2 / 256 )
ll_tmp2 = ( ll_tmp2 - ( ll_tmp3 * 256 ) )
ll_24bits = (256 * ll_tmp) + ll_tmp2
Else
//Reverse next char and add ll_tmp3 to the beginning to create our next 3 byte(24bit) buffer
ll_tmp = Asc( Mid( ls_data, ll_i, 1 ) )
ll_24bits = ( ll_tmp3 * 65536 ) + ( ( ll_tmp - ( Int( ll_tmp / 256 ) * 256 ) ) * 256 ) + ( Int( ll_tmp / 256 ) )
SetNull( ll_tmp3 )
End If
//Get each 6 bit indexes
ll_1 = Int( ll_24bits / 262144 ) //Shift 18 bits right to get first 6bit index
ll_24bits -= ll_1 * 262144 //remove first 6 bits from buffer
ll_2 = Int( ll_24bits / 4096 ) //Shift 12 bits right for 2nd
ll_24bits -= ll_2 * 4096 //Remove it from buffer
ll_3 = Int( ll_24bits / 64 ) //Shift 6 bits for third
ll_4 = ll_24bits - ( ll_3 * 64 ) // remove third and left with last 6 bit index
//Convert To base64
ls_ret += Mid( ls_base64, ll_1 + 1, 1 ) + Mid( ls_base64, ll_2 + 1, 1 ) + Mid( ls_base64, ll_3 + 1, 1 ) + + Mid( ls_base64, ll_4 + 1, 1 )
//Add a new line For Each 72 chars In dest
If Mod( Len( ls_ret ) - ll_newline_offset , 72 ) = 0 Then
ls_ret += "~r~n"
ll_newline_offset += 2
End If
Next
//Pad end
Choose Case Mod( Len( ls_data ) - 1, 3 )
Case 1 //8 bit final
ls_ret = Left( ls_ret, Len( ls_ret ) - 2 ) + "=="
Case 2 //16 bit final
ls_ret = Left( ls_ret, Len( ls_ret ) - 1 ) + "="
End Choose
return ls_ret
end function
Decode:
public function string of_dec_to_hex (long al_number);
// 0 <= n <= 15
// Converts integer from 0 - 15 into character hex representation
string sHexChar
Choose Case al_number
Case 10
sHexChar = 'A'
Case 11
sHexChar = 'B'
Case 12
sHexChar = 'C'
Case 13
sHexChar = 'D'
Case 14
sHexChar = 'E'
Case 15
sHexChar = 'F'
Case Else
sHexChar = String(al_number)
End Choose
Return sHexChar
end function
public function string of_convert_to_hex (long al_number);
// string of_convert_to_hex( long alNumber ), recursive:
// Recursive function to translate number into hex representation
If al_number > 15 Then
Return of_convert_to_hex( al_number / 16 ) + of_dec_to_hex( Mod( al_number, 16 ) )
Else
Return of_dec_to_hex( al_number )
End If
end function
public function long of_hex2long (string as_hex);
string ls_hex
integer i,length
long result = 0
length = len(as_hex)
ls_hex = Upper(as_hex)
FOR i = 1 to length
result += &
(Pos ('123456789ABCDEF', mid(ls_hex, i, 1)) * &
( 16 ^ ( length - i ) ))
NEXT
RETURN result
end function
public function string of_replace (string as_source, string as_replace, string as_with);
int ll_start = 1,ll_len
ll_len = len( as_replace )
ll_start = Pos( as_source, as_replace, ll_start )
Do While ll_start > 0
as_source = Replace( as_source, ll_start, ll_len, as_with )
ll_start = Pos( as_source, as_replace, ll_start + Len( as_with ) )
Loop
return as_source
end function
public function string of_decode_base64 (string as_base64);
string Base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
string ls_working
long dataLength, groupBegin, ll_mod
string sOut, pOut, ls_tmp
//remove white spaces, If any
ls_working = of_replace(as_base64, "~r", "")
ls_working = of_replace(as_base64, "~n", "")
ls_working = of_replace(ls_working, "~t", "")
ls_working = trim( ls_working )
dataLength = Len( ls_working )
If Mod(dataLength, 4) <> 0 Then
return ""
End If
//Now decode each group:
For groupBegin = 1 To dataLength Step 4
long numDataBytes, CharCounter, thisData, nGroup
char thisChar
//Each data group encodes up To 3 actual bytes.
numDataBytes = 3
nGroup = 0
For CharCounter = 0 To 3
/*
Convert each character into 6 bits of data, And add it To
an integer For temporary storage. If a character is a '=', there
is one fewer data byte. (There can only be a maximum of 2 '=' In
the whole string.)
*/
thisChar = Mid(ls_working, groupBegin + CharCounter, 1)
If thisChar = "=" Then
numDataBytes = numDataBytes - 1
thisData = 0
Else
thisData = Pos(Base64, thisChar, 1) - 1
End If
If thisData = -1 Then
return ""
End If
nGroup = 64 * nGroup + thisData
Next
//Hex splits the long To 6 groups with 4 bits
string ls_group
ls_group = of_convert_to_hex( nGroup )
//Add leading zeros
Do While Len(ls_group) < 6
ls_group = "0" + ls_group
Loop
//Converts two characters at a time accounting for Unicode and Little Endian
//Saves the third char for the next run
If ls_tmp <> "" Then
pOut = Char( of_hex2long( Mid( ls_group, 1, 2 ) + ls_tmp) )
pOut = pOut + Char( of_hex2long( Mid( ls_group, 5, 2 ) + Mid( ls_group, 3, 2) ) )
ls_tmp = ""
Else
pOut = Char( of_hex2long( Mid( ls_group, 3, 2 ) + Mid( ls_group, 1, 2) ) )
ls_tmp = Mid( ls_group, 5, 2 )
End If
//add numDataBytes characters To out string
sOut = sOut + Left(pOut, numDataBytes)
Next
return sOut
end function
Edit:
I got through with my Quiznos pretty quick today, so i decided to take a quick look at the comments about decoding base64 to a blob.
NOTE: I didn't spend very much time going over this (Im lazy!). I did however test it succesfully with two jpegs that where converted to base64 strings using C#'s Convert.ToBase64String
NOTE2: Encoding I did not even glance at. Your on your own there.
public function blob of_decode_base64_to_blob (string as_base64);
string ls_base_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
string ls_working
long ll_data_len, ll_start_from, ll_mod
string ls_tmp, ls_hex
long ll_num_bytes, ll_count, ll_this_pos, ll_dec
char lc_char
blob lb, lb_out
long ll_pos
ll_pos = 1
//remove white spaces, If any
ls_working = gnv_app.of_conv(as_base64, "~r", "")
ls_working = gnv_app.of_conv(as_base64, "~n", "")
ls_working = gnv_app.of_conv(ls_working, "~t", "")
ls_working = trim( ls_working )
ll_data_len = Len( ls_working )
If Mod(ll_data_len, 4) <> 0 Then
return lb
End If
lb = Blob(space(ll_data_len))
//Now decode each group:
For ll_start_from = 1 To ll_data_len Step 4
//Each data group encodes up To 3 actual bytes.
ll_num_bytes = 3
ll_dec = 0
For ll_count = 0 To 3
/*
Convert each character into 6 bits of data, And add it To
an integer For temporary storage. If a character is a '=', there
is one fewer data byte. (There can only be a maximum of 2 '=' In
the whole string.)
*/
lc_char = Mid(ls_working, ll_start_from + ll_count, 1)
If lc_char = "=" Then
ll_num_bytes = ll_num_bytes - 1
ll_this_pos = 0
Else
ll_this_pos = Pos(ls_base_chars, lc_char, 1) - 1
End If
If ll_this_pos = -1 Then
return lb
End If
ll_dec = 64 * ll_dec + ll_this_pos
Next
//Hex splits the long To 6 groups with 4 bits
ls_hex = of_convert_to_hex( ll_dec )
//Add leading zeros
Do While Len(ls_hex) < 6
ls_hex = "0" + ls_hex
Loop
//Converts two characters at a time accounting for Little Endian
//Saves the third char for the next run
If ls_tmp <> "" Then
//Add first two byte
BlobEdit(lb, ll_pos, of_hexlong( Mid( ls_hex, 1, 2 ) + ls_tmp))
ll_pos += 2
//Add next two bytes
BlobEdit(lb, ll_pos, of_hexlong( Mid( ls_hex, 5, 2 ) + Mid( ls_hex, 3, 2) ) )
ll_pos += 2
ls_tmp = ""
Else
//Add two bytes
BlobEdit(lb, ll_pos, of_hexlong( Mid( ls_hex, 3, 2 ) + Mid(ls_hex, 1, 2)) )
ll_pos += 2
//Save third
ls_tmp = Mid( ls_hex, 5, 2 )
End If
//Adjust position as needed
ll_pos = ll_pos - (3 - ll_num_bytes)
Next
lb_out = BlobMid(lb, 1, ll_pos)
return lb_out
end function