1
0
forked from aniani/vim

patch 9.1.0980: no support for base64 en-/decoding functions in Vim Script

Problem:  no support for base64 en-/decoding functions in Vim Script
          (networkhermit)
Solution: Add the base64_encode() and base64_decode() functions
          (Yegappan Lakshmanan)

fixes: #16291
closes: #16330

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Yegappan Lakshmanan
2024-12-30 10:29:44 +01:00
committed by Christian Brabandt
parent 48fa3198b7
commit 810785c689
8 changed files with 285 additions and 7 deletions

View File

@@ -28,6 +28,8 @@ static void f_balloon_show(typval_T *argvars, typval_T *rettv);
static void f_balloon_split(typval_T *argvars, typval_T *rettv);
# endif
#endif
static void f_base64_encode(typval_T *argvars, typval_T *rettv);
static void f_base64_decode(typval_T *argvars, typval_T *rettv);
static void f_bindtextdomain(typval_T *argvars, typval_T *rettv);
static void f_byte2line(typval_T *argvars, typval_T *rettv);
static void f_call(typval_T *argvars, typval_T *rettv);
@@ -1834,6 +1836,10 @@ static funcentry_T global_functions[] =
NULL
#endif
},
{"base64_decode", 1, 1, FEARG_1, arg1_string,
ret_blob, f_base64_decode},
{"base64_encode", 1, 1, FEARG_1, arg1_blob,
ret_string, f_base64_encode},
{"bindtextdomain", 2, 2, 0, arg2_string,
ret_bool, f_bindtextdomain},
{"blob2list", 1, 1, FEARG_1, arg1_blob,
@@ -3479,6 +3485,182 @@ f_balloon_split(typval_T *argvars, typval_T *rettv UNUSED)
# endif
#endif
// Base64 character set
static const char_u base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Base64 decoding table (initialized in init_base64_dec_table() below)
static char_u base64_dec_table[256];
/*
* Initialize the base64 decoding table
*/
static void
init_base64_dec_table(void)
{
static int base64_dec_tbl_initialized = FALSE;
if (base64_dec_tbl_initialized)
return;
// Unsupported characters are set to 0xFF
vim_memset(base64_dec_table, 0xFF, sizeof(base64_dec_table));
// Initialize the index for the base64 alphabets
for (size_t i = 0; i < sizeof(base64_table) - 1; i++)
base64_dec_table[(char_u)base64_table[i]] = (char_u)i;
// base64 padding character
base64_dec_table['='] = 0;
base64_dec_tbl_initialized = TRUE;
}
/*
* Encode the bytes in "blob" using base-64 encoding.
*/
static char_u *
base64_encode(blob_T *blob)
{
size_t input_len = blob->bv_ga.ga_len;
size_t encoded_len = ((input_len + 2) / 3) * 4;
char_u *data = blob->bv_ga.ga_data;
char_u *encoded = alloc(encoded_len + 1);
if (encoded == NULL)
return NULL;
size_t i, j;
for (i = 0, j = 0; i < input_len;)
{
int_u octet_a = i < input_len ? data[i++] : 0;
int_u octet_b = i < input_len ? data[i++] : 0;
int_u octet_c = i < input_len ? data[i++] : 0;
int_u triple = (octet_a << 16) | (octet_b << 8) | octet_c;
encoded[j++] = base64_table[(triple >> 18) & 0x3F];
encoded[j++] = base64_table[(triple >> 12) & 0x3F];
encoded[j++] = (!octet_b && i >= input_len) ? '='
: base64_table[(triple >> 6) & 0x3F];
encoded[j++] = (!octet_c && i >= input_len) ? '='
: base64_table[triple & 0x3F];
}
encoded[j] = NUL;
return encoded;
}
/*
* Decode the string "data" using base-64 encoding.
*/
static void
base64_decode(const char_u *data, blob_T *blob)
{
size_t input_len = STRLEN(data);
if (input_len == 0)
return;
if (input_len % 4 != 0)
{
// Invalid input length
semsg(_(e_invalid_argument_str), data);
return;
}
init_base64_dec_table();
size_t decoded_len = (input_len / 4) * 3;
if (data[input_len - 1] == '=')
decoded_len--;
if (data[input_len - 2] == '=')
decoded_len--;
size_t i, j;
for (i = 0, j = 0; i < input_len;)
{
int_u sextet_a = base64_dec_table[(char_u)data[i++]];
int_u sextet_b = base64_dec_table[(char_u)data[i++]];
int_u sextet_c = base64_dec_table[(char_u)data[i++]];
int_u sextet_d = base64_dec_table[(char_u)data[i++]];
if (sextet_a == 0xFF || sextet_b == 0xFF || sextet_c == 0xFF
|| sextet_d == 0xFF)
{
// Invalid character
semsg(_(e_invalid_argument_str), data);
ga_clear(&blob->bv_ga);
return;
}
int_u triple = (sextet_a << 18) | (sextet_b << 12)
| (sextet_c << 6) | sextet_d;
if (j < decoded_len)
{
ga_append(&blob->bv_ga, (triple >> 16) & 0xFF);
j++;
}
if (j < decoded_len)
{
ga_append(&blob->bv_ga, (triple >> 8) & 0xFF);
j++;
}
if (j < decoded_len)
{
ga_append(&blob->bv_ga, triple & 0xFF);
j++;
}
if (j == decoded_len)
{
// Check for invalid padding bytes (based on the
// "Base64 Malleability in Practice" ACM paper).
if ((data[input_len - 2] == '=' && ((sextet_b & 0xF) != 0))
|| ((data[input_len - 1] == '=') && ((sextet_c & 0x3) != 0)))
{
semsg(_(e_invalid_argument_str), data);
ga_clear(&blob->bv_ga);
return;
}
}
}
}
/*
* "base64_decode(string)" function
*/
static void
f_base64_decode(typval_T *argvars, typval_T *rettv)
{
if (check_for_string_arg(argvars, 0) == FAIL)
return;
if (rettv_blob_alloc(rettv) == FAIL)
return;
char_u *str = tv_get_string_chk(&argvars[0]);
if (str != NULL)
base64_decode(str, rettv->vval.v_blob);
}
/*
* "base64_encode(blob)" function
*/
static void
f_base64_encode(typval_T *argvars, typval_T *rettv)
{
if (check_for_blob_arg(argvars, 0) == FAIL)
return;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
blob_T *blob = argvars->vval.v_blob;
if (blob != NULL)
rettv->vval.v_string = base64_encode(blob);
}
/*
* Get the buffer from "arg" and give an error and return NULL if it is not
* valid.

View File

@@ -4206,4 +4206,55 @@ func Test_getcellpixels_gui()
endif
endfunc
func Str2Blob(s)
return list2blob(str2list(a:s))
endfunc
func Blob2Str(b)
return list2str(blob2list(a:b))
endfunc
" Test for the base64_encode() and base64_decode() functions
func Test_base64_encoding()
let lines =<< trim END
#" Test for encoding/decoding the RFC-4648 alphabets
VAR s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for i in range(64)
call assert_equal($'{s[i]}A==', base64_encode(list2blob([i << 2])))
call assert_equal(list2blob([i << 2]), base64_decode($'{s[i]}A=='))
endfor
#" Test for encoding with padding
call assert_equal('TQ==', base64_encode(g:Str2Blob("M")))
call assert_equal('TWE=', base64_encode(g:Str2Blob("Ma")))
call assert_equal('TWFu', g:Str2Blob("Man")->base64_encode())
call assert_equal('', base64_encode(0z))
call assert_equal('', base64_encode(g:Str2Blob("")))
#" Test for decoding with padding
call assert_equal('light work.', g:Blob2Str(base64_decode("bGlnaHQgd29yay4=")))
call assert_equal('light work', g:Blob2Str(base64_decode("bGlnaHQgd29yaw==")))
call assert_equal('light wor', g:Blob2Str("bGlnaHQgd29y"->base64_decode()))
call assert_equal(0z00, base64_decode("===="))
call assert_equal(0z, base64_decode(""))
#" Test for invalid padding
call assert_equal('Hello', g:Blob2Str(base64_decode("SGVsbG8=")))
call assert_fails('call base64_decode("SGVsbG9=")', 'E475:')
call assert_fails('call base64_decode("SGVsbG9")', 'E475:')
call assert_equal('Hell', g:Blob2Str(base64_decode("SGVsbA==")))
call assert_fails('call base64_decode("SGVsbA=")', 'E475:')
call assert_fails('call base64_decode("SGVsbA")', 'E475:')
call assert_fails('call base64_decode("SGVsbA====")', 'E475:')
#" Error case
call assert_fails('call base64_decode("b")', 'E475: Invalid argument: b')
call assert_fails('call base64_decode("<<==")', 'E475: Invalid argument: <<==')
call assert_fails('call base64_encode([])', 'E1238: Blob required for argument 1')
call assert_fails('call base64_decode([])', 'E1174: String required for argument 1')
END
call v9.CheckLegacyAndVim9Success(lines)
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
980,
/**/
979,
/**/