/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) */ /* Enable POSIX features */ #if !defined(_XOPEN_SOURCE) # define _XOPEN_SOURCE 600 #endif #ifndef _DEFAULT_SOURCE # define _DEFAULT_SOURCE #endif /* On older systems we need to define _BSD_SOURCE. * _DEFAULT_SOURCE is an alias for that. */ #ifndef _BSD_SOURCE # define _BSD_SOURCE #endif #include #include #include #include /* Internal headers */ #include "ua_pubsub_networkmessage.h" static UA_StatusCode encode(const UA_ByteString *buf, UA_ByteString *out, const UA_DataType *type) { void *data = malloc(type->memSize); if(!data) return UA_STATUSCODE_BADOUTOFMEMORY; UA_StatusCode retval = UA_decodeBinary(buf, data, type, NULL); if(retval != UA_STATUSCODE_GOOD) { free(data); return retval; } retval = UA_encodeJson(data, type, out, NULL); UA_delete(data, type); if(retval != UA_STATUSCODE_GOOD) { UA_ByteString_clear(out); return retval; } return UA_STATUSCODE_GOOD; } static UA_StatusCode decode(const UA_ByteString *buf, UA_ByteString *out, const UA_DataType *type) { /* Allocate memory for the type */ void *data = malloc(type->memSize); if(!data) return UA_STATUSCODE_BADOUTOFMEMORY; /* Decode JSON */ const UA_DecodeJsonOptions opt = {NULL}; UA_StatusCode retval = UA_decodeJson(buf, data, type, &opt); if(retval != UA_STATUSCODE_GOOD) { free(data); return retval; } /* Encode Binary. Internally allocates the buffer upon success */ retval = UA_encodeBinary(data, type, out); /* Clean up */ UA_delete(data, type); return retval; } #ifdef UA_ENABLE_PUBSUB static UA_StatusCode encodeNetworkMessage(const UA_ByteString *buf, UA_ByteString *out) { size_t offset = 0; UA_NetworkMessage msg; UA_StatusCode retval = UA_NetworkMessage_decodeBinary(buf, &offset, &msg); if(retval != UA_STATUSCODE_GOOD) return retval; if(offset != buf->length) { UA_NetworkMessage_clear(&msg); fprintf(stderr, "Input buffer not completely read\n"); return UA_STATUSCODE_BADINTERNALERROR; } size_t jsonLength = UA_NetworkMessage_calcSizeJson(&msg, NULL, 0, NULL, 0, true); retval = UA_ByteString_allocBuffer(out, jsonLength); if(retval != UA_STATUSCODE_GOOD) { UA_NetworkMessage_clear(&msg); return retval; } uint8_t *bufPos = &out->data[0]; const uint8_t *bufEnd = &out->data[out->length]; retval = UA_NetworkMessage_encodeJson(&msg, &bufPos, &bufEnd, NULL, 0, NULL, 0, true); UA_NetworkMessage_clear(&msg); if(retval != UA_STATUSCODE_GOOD) { UA_ByteString_clear(out); return retval; } out->length = (size_t)((uintptr_t)bufPos - (uintptr_t)out->data); return UA_STATUSCODE_GOOD; } static UA_StatusCode decodeNetworkMessage(const UA_ByteString *buf, UA_ByteString *out) { UA_NetworkMessage msg; UA_StatusCode retval = UA_NetworkMessage_decodeJson(&msg, buf); if(retval != UA_STATUSCODE_GOOD) return retval; size_t binLength = UA_NetworkMessage_calcSizeBinary(&msg, NULL); retval = UA_ByteString_allocBuffer(out, binLength); if(retval != UA_STATUSCODE_GOOD) { UA_NetworkMessage_clear(&msg); return retval; } uint8_t *bufPos = &out->data[0]; const uint8_t *bufEnd = &out->data[out->length]; retval = UA_NetworkMessage_encodeBinary(&msg, &bufPos, bufEnd, NULL); UA_NetworkMessage_clear(&msg); if(retval != UA_STATUSCODE_GOOD) { UA_ByteString_clear(out); return retval; } out->length = (size_t)((uintptr_t)bufPos - (uintptr_t)out->data); return UA_STATUSCODE_GOOD; } #endif static void usage(void) { printf("Usage: ua2json [encode|decode] [-t dataType] [-o outputFile] [inputFile]\n" "- encode/decode: Translate UA binary input to UA JSON / " "Translate UA JSON input to UA binary (required)\n" "- dataType: UA DataType of the input (default: Variant)\n" "- outputFile: Output target (default: write to stdout)\n" "- inputFile: Input source (default: read from stdin)\n"); } int main(int argc, char **argv) { UA_Boolean encode_option = true; #ifdef UA_ENABLE_PUBSUB UA_Boolean pubsub = false; #endif const char *datatype_option = "Variant"; const char *input_option = NULL; const char *output_option = NULL; UA_ByteString outbuf = UA_BYTESTRING_NULL; UA_ByteString buf = UA_BYTESTRING_NULL; FILE *in = stdin; FILE *out = stdout; int retcode = -1; /* Read the command line options */ if(argc < 2) { usage(); return 0; } if(strcmp(argv[1], "encode") == 0) { encode_option = true; } else if(strcmp(argv[1], "decode") == 0) { encode_option = false; } else { fprintf(stderr, "Error: The first argument must be \"encode\" or \"decode\"\n"); return -1; } for(int argpos = 2; argpos < argc; argpos++) { if(strcmp(argv[argpos], "--help") == 0) { usage(); return 0; } if(strcmp(argv[argpos], "-t") == 0) { if(argpos + 1 == argc) { usage(); return -1; } argpos++; datatype_option = argv[argpos]; continue; } if(strcmp(argv[argpos], "-o") == 0) { if(argpos + 1 == argc) { usage(); return -1; } argpos++; output_option = argv[argpos]; continue; } if(argpos + 1 == argc) { input_option = argv[argpos]; continue; } usage(); return -1; } /* Find the data type */ const UA_DataType *type = NULL; #ifdef UA_ENABLE_PUBSUB if(strcmp(datatype_option, "PubSub") == 0) { pubsub = true; } else #endif { for(size_t i = 0; i < UA_TYPES_COUNT; ++i) { if(strcmp(datatype_option, UA_TYPES[i].typeName) == 0) { type = &UA_TYPES[i]; break; } } if(!type) { fprintf(stderr, "Error: Datatype not found\n"); return -1; } } /* Open files */ if(input_option) { in = fopen(input_option, "rb"); if(!in) { fprintf(stderr, "Could not open input file %s\n", input_option); goto cleanup; } } if(output_option) { out = fopen(output_option, "wb"); if(!out) { fprintf(stderr, "Could not open output file %s\n", output_option); goto cleanup; } } /* Read input until EOF */ size_t pos = 0; size_t length = 128; while(true) { if(pos >= buf.length) { length = length * 8; UA_Byte *r = (UA_Byte*)realloc(buf.data, length); if(!r) { fprintf(stderr, "Out of memory\n"); goto cleanup; } buf.length = length; buf.data = r; } ssize_t c = read(fileno(in), &buf.data[pos], length - pos); if(c == 0) break; if(c < 0) { fprintf(stderr, "Reading from input failed\n"); goto cleanup; } pos += (size_t)c; } if(pos == 0) { fprintf(stderr, "No input\n"); goto cleanup; } buf.length = pos; /* Convert */ UA_StatusCode result = UA_STATUSCODE_BADNOTIMPLEMENTED; #ifdef UA_ENABLE_PUBSUB if(pubsub && encode_option) { result = encodeNetworkMessage(&buf, &outbuf); } else if(pubsub) { result = decodeNetworkMessage(&buf, &outbuf); } else #endif if(encode_option) { result = encode(&buf, &outbuf, type); } else { result = decode(&buf, &outbuf, type); } if(result != UA_STATUSCODE_GOOD) { fprintf(stderr, "Error: Parsing failed with code %s\n", UA_StatusCode_name(result)); goto cleanup; } /* Print the output and quit */ fwrite(outbuf.data, 1, outbuf.length, out); retcode = 0; cleanup: UA_ByteString_clear(&buf); UA_ByteString_clear(&outbuf); if(in != stdin && in) fclose(in); if(out != stdout && out) fclose(out); return retcode; }