blog icon indicating copy to clipboard operation
blog copied to clipboard

Base64 encoding and decoding

Open worksofliam opened this issue 5 years ago • 5 comments

Base64 is wonderful, at times. It can be great for serialization of small binary files, but not large ones. If you use base64 for serialization on binary files and then storing them as text into a database or sending that text in a web request - look into the BLOB type and HTTP POST with upload.

Using Embedded SQL, we are now able to encode and decode into base64 on the fly using SYSTOOLS.BASE64ENCODE and SYSTOOLS.BASE64DECODE. When you use these functions, the host variables you pass in should always be varying length (varchar type). If you use a regular character type, it will also encode all the whitespace at the end of the variable.

Dcl-S VarcharField Varchar(256);
VarcharField = 'Hello WorksOfBarry blog!!';

EXEC SQL SET :VarcharField = SYSTOOLS.BASE64ENCODE(:VarcharField);
EXEC SQL SET :VarcharField = SYSTOOLS.BASE64DECODE(:VarcharField);

This will encode the VarcharField variable, and then assign the encoding to it - also the same for decode. Of course, you can change where it assigns to and what it encodes.

CCSID Support

These two functions also support CCSIDs, including 1208 - which is used everywhere on the web, meaning it may prove useful for web services. Usually, when you encode it will look at the CCSID of the variable, and then the job (which comes off the system anyway). RPG gives the ability to change the CCSID of a variable using the CCSID option when defining that variable. Notice below that I am using *UTF8, but you can also use 1208 - or any CCSID you want. Check out the documentation for the CCSID keyword to see the available constants within this keyword.

Dcl-S UTF8Variable Varchar(100) Inz('') CCSID(*UTF8);

So in our example below, we have a normal varying length field which we want to convert to UTF-8 base64. To do that, we need a second variable with a CCSID of UTF-8. When we assign our regular variable to our variable with a certain CCSID, RPG will do the CCSID conversion for us - great! From there, we can simply use SYSTOOLS.BASE64ENCODE against the UTF-8 field to get our UTF-8 base64 encoding.

Dcl-S SomeCharVar Varchar(50);   
Dcl-S ToBeEncoded Varchar(100) Inz('') CCSID(1208); 
 
SomeCharVar = 'hello db2 summit'; 
ToBeEncoded = SomeCharVar; //Convert it to 1208 
 
//Encode based on 1208 
EXEC SQL SET :ToBeEncoded = SYSTOOLS.BASE64ENCODE(:ToBeEncoded);

//By this point of the program, ToBeEncoded now contains UTF-8 base64.

That's it! You can out more at these links:

worksofliam avatar Jul 07 '19 01:07 worksofliam

Just tried the above example and it doesn't work:

Unable to retrieve query options file.
**** Starting optimizer debug message for query .
**** Ending debug message for query .
ODP created.
Blocking used for query.
User-defined function error on member QSQPTABL.
User-defined function error on member QSQPTABL.
ODP deleted.
Character conversion between CCSID 65535 and CCSID 1200 not valid.

priceaj avatar Nov 12 '19 17:11 priceaj

Character conversion between CCSID 65535 and CCSID 1200 not valid.

Try this command before running the program with the conversion.

CHGJOB CCSID(37)

mooska avatar Nov 12 '19 17:11 mooska

Character conversion between CCSID 65535 and CCSID 1200 not valid.

Try this command before running the program with the conversion.

CHGJOB CCSID(37)

I just came back to say that I needed to change my job CCSID, wish I'd have checked my emails 30 mins ago!! Thanks @mooska

priceaj avatar Nov 12 '19 17:11 priceaj

Just to add to the above, base64decode doesn't like converting from 1208 even with job CCSID changed to 37 (as per the above workaround to get base64encode working)

running

DCL-S VarCharUTF VARCHAR(256) CCSID(*UTF8); 
DCL-S VarCharUTF_enc VARCHAR(256) CCSID(*UTF8);
DCL-S VarCharUTF_dec VARCHAR(256) CCSID(*UTF8); 

ChangeCCSID('37'); 
VarCharUTF = 'user:pass';`

EXEC SQL
   SET :varcharutf_enc = systools.base64encode(:varcharutf); 
// Returns dXNlcjpwYXNz

EXEC SQL
  SET :varcharutf_dec = systools.base64decode(:varcharutf_enc);  
// Error - Character conversion between CCSID 65535 and CCSID 1208 not valid

ChangeCCSID('*SYSVAL'); 
*inlr = *ON;

to work around this I had to do the following:

DCL-PR Hex EXTPROC('cvtch');
  szTgt_short CHAR(32767) CCSID(*HEX) OPTIONS(*VARSIZE);
  szSrc_long CHAR(65534) CONST OPTIONS(*VARSIZE);
  nSrcLen INT(10) VALUE;
END-PR ;        

DCL-S VarCharUTF VARCHAR(256) CCSID(*UTF8); 
DCL-S VarCharUTF_enc VARCHAR(256) CCSID(*UTF8);
DCL-S varcharUTF_hexstring VARCHAR(512); 
DCL-S varcharUTF_hex CHAR(256) CCSID(*HEX); 

ChangeCCSID('37'); 
VarCharUTF = 'user:pass';`

EXEC SQL
   SET :varcharutf_enc = systools.base64encode(:varcharutf); 
// Returns dXNlcjpwYXNz

   // Grab HEX output from Base64Decode 
   EXEC SQL
     SET :varcharutf_hexstring =
       HEX(systools.base64decode(:varcharutf_enc));
   // Returns 757365723A70617373

     Hex(VarCharUTF_hex :
         %trim(varcharUTF_hexstring) :
         %size(%trim(varcharUTF_hexstring)));
   // eval VarCharUTF_hex  :x returns 75736572 3A706173 73404040

      VarCharUTF = %trim(VarCharUTF_hex);
   // Returns user:pass                 

ChangeCCSID('*SYSVAL'); 
*inlr = *ON;

where ChangeCCSID just runs the change job command

If there is a better way around this, I would be glad to hear it!

NB: The reason I added the CCSID(*HEX) line to cvtch was because I was also trying this with CCSID(37) fields in the same program, to make it compatible with both methods I read into a Variable with CCSID(*HEX) then read into the relevant char field with CCSID(37) or CCSID(*UTF8)

priceaj avatar Nov 14 '19 16:11 priceaj

I have retrieved a data string that is encoded in base64 and attempting to decode to binary to display as a document as a .png

Any assistance would be greatly appreciated.

Tmann62 avatar Nov 11 '20 15:11 Tmann62