Repository: nifi-minifi-cpp
Updated Branches:
  refs/heads/master 15d9a6911 -> 20339fbee


MINIFICPP-469 Added encode/decode base64 EL functions

This closes #307.

Signed-off-by: Aldrin Piri <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/commit/20339fbe
Tree: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/tree/20339fbe
Diff: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/diff/20339fbe

Branch: refs/heads/master
Commit: 20339fbee1d915b03baeda288d9bae94d69f07f1
Parents: 15d9a69
Author: Andrew I. Christianson <[email protected]>
Authored: Tue Apr 24 13:06:46 2018 -0400
Committer: Aldrin Piri <[email protected]>
Committed: Mon May 14 16:37:12 2018 -0400

----------------------------------------------------------------------
 EXPRESSIONS.md                                  |  38 +++-
 extensions/expression-language/Expression.cpp   |  32 ++++
 extensions/expression-language/base64.h         | 188 +++++++++++++++++++
 .../ExpressionLanguageTests.cpp                 |  24 +++
 4 files changed, 280 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/20339fbe/EXPRESSIONS.md
----------------------------------------------------------------------
diff --git a/EXPRESSIONS.md b/EXPRESSIONS.md
index f2eebb2..d5a34bb 100644
--- a/EXPRESSIONS.md
+++ b/EXPRESSIONS.md
@@ -215,6 +215,8 @@ token, filename.
 - [`unescapeCsv`](#unescapecsv)
 - [`urlEncode`](#urlencode)
 - [`urlDecode`](#urldecode)
+- [`base64Encode`](#base64encode)
+- [`base64Decode`](#base64decode)
 
 ### Subjectless Functions
 
@@ -235,8 +237,6 @@ token, filename.
 - `escapeHtml4`
 - `unescapeHtml3`
 - `unescapeHtml4`
-- `base64Encode`
-- `base64Decode`
 
 ### Date Manipulation
 
@@ -1477,6 +1477,40 @@ If we have a URL-Encoded attribute named "url" with the 
value
 "some%20value%20with%20spaces", then the Expression `${url:urlDecode()}` will
 return "some value with spaces".
 
+### base64Encode
+
+**Description**: Returns a Base64 encoded string. This is useful for being able
+to transfer binary data as ascii.
+
+**Subject Type**: String
+
+**Arguments**: No arguments
+
+**Return Type**: String
+
+**Examples**:
+
+We can Base64-Encoded an attribute named "payload" by using the Expression
+`${payload:base64Encode()}` If the attribute payload had a value of
+"admin:admin" then the Expression `${payload:base64Encode()}` will return
+"YWRtaW46YWRtaW4=".
+
+### base64Decode
+
+**Description**: Reverses the Base64 encoding on given string.
+
+**Subject Type**: String
+
+**Arguments**: No arguments
+
+**Return Type**: String
+
+**Examples**:
+
+If we have a Base64-Encoded attribute named "payload" with the value
+"YWRtaW46YWRtaW4=", then the Expression `${payload:base64Decode()}` will return
+"admin:admin".
+
 ## Subjectless Functions
 
 While the majority of functions in the Expression Language are called by using

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/20339fbe/extensions/expression-language/Expression.cpp
----------------------------------------------------------------------
diff --git a/extensions/expression-language/Expression.cpp 
b/extensions/expression-language/Expression.cpp
index b26a515..4a93b1c 100644
--- a/extensions/expression-language/Expression.cpp
+++ b/extensions/expression-language/Expression.cpp
@@ -31,6 +31,7 @@
 #include <curl/curl.h>
 #include <netdb.h>
 #include <arpa/inet.h>
+#include "base64.h"
 #include "Driver.h"
 
 namespace org {
@@ -1345,6 +1346,33 @@ Value expr_urlDecode(const std::vector<Value> &args) {
   }
 }
 
+Value expr_base64Encode(const std::vector<Value> &args) {
+  auto arg_0 = args[0].asString();
+  char *b64_out = nullptr;
+  auto b64_len = Curl_base64_encode(arg_0.c_str(), arg_0.length(), &b64_out);
+  if (b64_out) {
+    std::string result(b64_out, b64_len);
+    free(b64_out);
+    return Value(result);
+  } else {
+    throw std::runtime_error("Failed to encode base64");
+  }
+}
+
+Value expr_base64Decode(const std::vector<Value> &args) {
+  auto arg_0 = args[0].asString();
+  unsigned char *decode_out = nullptr;
+  // size_t Curl_base64_decode(const char *src, unsigned char **outptr)
+  auto out_len = Curl_base64_decode(arg_0.c_str(), &decode_out);
+  if (decode_out) {
+    std::string result(reinterpret_cast<char *>(decode_out), out_len);
+    free(decode_out);
+    return Value(result);
+  } else {
+    throw std::runtime_error("Failed to encode base64");
+  }
+}
+
 #ifdef EXPRESSION_LANGUAGE_USE_REGEX
 
 Value expr_replace(const std::vector<Value> &args) {
@@ -1722,6 +1750,10 @@ Expression make_dynamic_function(const std::string 
&function_name,
     return make_dynamic_function_incomplete<expr_urlEncode>(function_name, 
args, 0);
   } else if (function_name == "urlDecode") {
     return make_dynamic_function_incomplete<expr_urlDecode>(function_name, 
args, 0);
+  } else if (function_name == "base64Encode") {
+    return make_dynamic_function_incomplete<expr_base64Encode>(function_name, 
args, 0);
+  } else if (function_name == "base64Decode") {
+    return make_dynamic_function_incomplete<expr_base64Decode>(function_name, 
args, 0);
 #ifdef EXPRESSION_LANGUAGE_USE_REGEX
   } else if (function_name == "replace") {
     return make_dynamic_function_incomplete<expr_replace>(function_name, args, 
2);

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/20339fbe/extensions/expression-language/base64.h
----------------------------------------------------------------------
diff --git a/extensions/expression-language/base64.h 
b/extensions/expression-language/base64.h
new file mode 100644
index 0000000..c2c48ba
--- /dev/null
+++ b/extensions/expression-language/base64.h
@@ -0,0 +1,188 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2007, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#ifndef NIFI_MINIFI_CPP_BASE64_H
+#define NIFI_MINIFI_CPP_BASE64_H
+
+/* ---- Base64 Encoding/Decoding Table --- */
+static const char table64[]=
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static void decodeQuantum(unsigned char *dest, const char *src)
+{
+  unsigned int x = 0;
+  int i;
+  char *found;
+
+  for(i = 0; i < 4; i++) {
+    if((found = strchr((char *)table64, src[i])))
+      x = (x << 6) + (unsigned int)(found - table64);
+    else if(src[i] == '=')
+      x = (x << 6);
+  }
+
+  dest[2] = (unsigned char)(x & 255);
+  x >>= 8;
+  dest[1] = (unsigned char)(x & 255);
+  x >>= 8;
+  dest[0] = (unsigned char)(x & 255);
+}
+
+
+/*
+ * Curl_base64_decode()
+ *
+ * Given a base64 string at src, decode it and return an allocated memory in
+ * the *outptr. Returns the length of the decoded data.
+ */
+size_t Curl_base64_decode(const char *src, unsigned char **outptr)
+{
+  int length = 0;
+  int equalsTerm = 0;
+  int i;
+  int numQuantums;
+  unsigned char lastQuantum[3];
+  size_t rawlen=0;
+  unsigned char *newstr;
+
+  *outptr = NULL;
+
+  while((src[length] != '=') && src[length])
+    length++;
+  /* A maximum of two = padding characters is allowed */
+  if(src[length] == '=') {
+    equalsTerm++;
+    if(src[length+equalsTerm] == '=')
+      equalsTerm++;
+  }
+  numQuantums = (length + equalsTerm) / 4;
+
+  /* Don't allocate a buffer if the decoded length is 0 */
+  if (numQuantums <= 0)
+    return 0;
+
+  rawlen = (numQuantums * 3) - equalsTerm;
+
+  /* The buffer must be large enough to make room for the last quantum
+  (which may be partially thrown out) and the zero terminator. */
+  newstr = static_cast<unsigned char *>(malloc(rawlen+4));
+  if(!newstr)
+    return 0;
+
+  *outptr = newstr;
+
+  /* Decode all but the last quantum (which may not decode to a
+  multiple of 3 bytes) */
+  for(i = 0; i < numQuantums - 1; i++) {
+    decodeQuantum((unsigned char *)newstr, src);
+    newstr += 3; src += 4;
+  }
+
+  /* This final decode may actually read slightly past the end of the buffer
+  if the input string is missing pad bytes.  This will almost always be
+  harmless. */
+  decodeQuantum(lastQuantum, src);
+  for(i = 0; i < 3 - equalsTerm; i++)
+    newstr[i] = lastQuantum[i];
+
+  newstr[i] = 0; /* zero terminate */
+  return rawlen;
+}
+
+/*
+ * Curl_base64_encode()
+ *
+ * Returns the length of the newly created base64 string. The third argument
+ * is a pointer to an allocated area holding the base64 data. If something
+ * went wrong, -1 is returned.
+ *
+ */
+size_t Curl_base64_encode(const char *inp, size_t insize, char **outptr)
+{
+  unsigned char ibuf[3];
+  unsigned char obuf[4];
+  int i;
+  int inputparts;
+  char *output;
+  char *base64data;
+
+  char *indata = (char *)inp;
+
+  *outptr = NULL; /* set to NULL in case of failure before we reach the end */
+
+  if(0 == insize)
+    insize = strlen(indata);
+
+  base64data = output = (char*)malloc(insize*4/3+4);
+  if(NULL == output)
+    return 0;
+
+  while(insize > 0) {
+    for (i = inputparts = 0; i < 3; i++) {
+      if(insize > 0) {
+        inputparts++;
+        ibuf[i] = *indata;
+        indata++;
+        insize--;
+      }
+      else
+        ibuf[i] = 0;
+    }
+
+    obuf[0] = (unsigned char)  ((ibuf[0] & 0xFC) >> 2);
+    obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \
+                               ((ibuf[1] & 0xF0) >> 4));
+    obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \
+                               ((ibuf[2] & 0xC0) >> 6));
+    obuf[3] = (unsigned char)   (ibuf[2] & 0x3F);
+
+    switch(inputparts) {
+      case 1: /* only one byte read */
+        snprintf(output, 5, "%c%c==",
+                 table64[obuf[0]],
+                 table64[obuf[1]]);
+        break;
+      case 2: /* two bytes read */
+        snprintf(output, 5, "%c%c%c=",
+                 table64[obuf[0]],
+                 table64[obuf[1]],
+                 table64[obuf[2]]);
+        break;
+      default:
+        snprintf(output, 5, "%c%c%c%c",
+                 table64[obuf[0]],
+                 table64[obuf[1]],
+                 table64[obuf[2]],
+                 table64[obuf[3]] );
+        break;
+    }
+    output += 4;
+  }
+  *output=0;
+  *outptr = base64data; /* make it return the actual data memory */
+
+  return strlen(base64data); /* return the length of the new data */
+}
+/* ---- End of Base64 Encoding ---- */
+
+#endif //NIFI_MINIFI_CPP_BASE64_H

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/20339fbe/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
----------------------------------------------------------------------
diff --git 
a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp 
b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
index 5c331be..3d99fd0 100644
--- a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
+++ b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
@@ -1245,3 +1245,27 @@ TEST_CASE("Length", "[expressionLength]") {  // NOLINT
   flow_file_a->addAttribute("message", "a brand new filename.txt");
   REQUIRE(24 == expr({flow_file_a}).asUnsignedLong());
 }
+
+TEST_CASE("Encode B64", "[expressionEncodeB64]") {  // NOLINT
+  auto expr = expression::compile("${message:base64Encode()}");
+
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  flow_file_a->addAttribute("message", "admin:admin");
+  REQUIRE("YWRtaW46YWRtaW4=" == expr({flow_file_a}).asString());
+}
+
+TEST_CASE("Decode B64", "[expressionDecodeB64]") {  // NOLINT
+  auto expr = expression::compile("${message:base64Decode()}");
+
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  flow_file_a->addAttribute("message", "YWRtaW46YWRtaW4=");
+  REQUIRE("admin:admin" == expr({flow_file_a}).asString());
+}
+
+TEST_CASE("Encode Decode B64", "[expressionEncodeDecodeB64]") {  // NOLINT
+  auto expr = expression::compile("${message:base64Encode():base64Decode()}");
+
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  flow_file_a->addAttribute("message", "Zero > One < \"two!\" & 'true'");
+  REQUIRE("Zero > One < \"two!\" & 'true'" == expr({flow_file_a}).asString());
+}

Reply via email to