From 34a3483d8f875a9a04ae117eaafc57ffee55349c Mon Sep 17 00:00:00 2001 From: rublon-jfr Date: Fri, 28 Oct 2022 11:40:17 +0200 Subject: [PATCH] RDEV-3625: Remove unused RPM module --- SSH/RPM/buildRublonPamRpm.sh | 27 - SSH/RPM/login_rublon.te | 8 - SSH/RPM/rublonPam-1.0-1.x86_64.rpm | Bin 27284 -> 0 bytes SSH/RPM/rublonPam-1.0/.config | 4 - SSH/RPM/rublonPam-1.0/Makefile | 14 - SSH/RPM/rublonPam-1.0/lib/cJSON.c | 2933 ------------------ SSH/RPM/rublonPam-1.0/lib/cJSON.h | 277 -- SSH/RPM/rublonPam-1.0/lib/cfg_parse.c | 568 ---- SSH/RPM/rublonPam-1.0/lib/cfg_parse.h | 94 - SSH/RPM/rublonPam-1.0/lib/qrcodegen.c | 1025 ------ SSH/RPM/rublonPam-1.0/lib/qrcodegen.h | 311 -- SSH/RPM/rublonPam-1.0/login_rublon.te | 10 - SSH/RPM/rublonPam-1.0/rublonPam.c | 69 - SSH/RPM/rublonPam-1.0/src/coreHandler.c | 398 --- SSH/RPM/rublonPam-1.0/src/misc.h | 35 - SSH/RPM/rublonPam-1.0/src/pamApp.c | 152 - SSH/RPM/rublonPam-1.0/src/signatureWrapper.c | 62 - SSH/RPM/rublonPam.spec | 43 - SSH/RPM/rublonPam.tar.gz | Bin 43049 -> 0 bytes 19 files changed, 6030 deletions(-) delete mode 100644 SSH/RPM/buildRublonPamRpm.sh delete mode 100644 SSH/RPM/login_rublon.te delete mode 100644 SSH/RPM/rublonPam-1.0-1.x86_64.rpm delete mode 100644 SSH/RPM/rublonPam-1.0/.config delete mode 100644 SSH/RPM/rublonPam-1.0/Makefile delete mode 100644 SSH/RPM/rublonPam-1.0/lib/cJSON.c delete mode 100644 SSH/RPM/rublonPam-1.0/lib/cJSON.h delete mode 100644 SSH/RPM/rublonPam-1.0/lib/cfg_parse.c delete mode 100644 SSH/RPM/rublonPam-1.0/lib/cfg_parse.h delete mode 100644 SSH/RPM/rublonPam-1.0/lib/qrcodegen.c delete mode 100644 SSH/RPM/rublonPam-1.0/lib/qrcodegen.h delete mode 100644 SSH/RPM/rublonPam-1.0/login_rublon.te delete mode 100644 SSH/RPM/rublonPam-1.0/rublonPam.c delete mode 100644 SSH/RPM/rublonPam-1.0/src/coreHandler.c delete mode 100644 SSH/RPM/rublonPam-1.0/src/misc.h delete mode 100644 SSH/RPM/rublonPam-1.0/src/pamApp.c delete mode 100644 SSH/RPM/rublonPam-1.0/src/signatureWrapper.c delete mode 100644 SSH/RPM/rublonPam.spec delete mode 100644 SSH/RPM/rublonPam.tar.gz diff --git a/SSH/RPM/buildRublonPamRpm.sh b/SSH/RPM/buildRublonPamRpm.sh deleted file mode 100644 index d786dcd..0000000 --- a/SSH/RPM/buildRublonPamRpm.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -#packages for rpm builder -sudo yum install -y gcc rpm-build rpm-devel rpmlint make python bash coreutils diffutils patch rpmdevtools tree -#packages for rublonPam -sudo yum install -y curl-devel openssl-devel pam-devel policycoreutils-python - -#sudo service sshd restart - -#create rpm package struct -rpmdev-setuptree - -cp -R /home/vagrant/Rublon-Linux/SSH/PAM/src /home/vagrant/Rublon-Linux/SSH/RPM/rublonPam-1.0/ -cp -R /home/vagrant/Rublon-Linux/SSH/PAM/lib /home/vagrant/Rublon-Linux/SSH/RPM/rublonPam-1.0/ -cp /home/vagrant/Rublon-Linux/SSH/PAM/rublonPam.c /home/vagrant/Rublon-Linux/SSH/RPM/rublonPam-1.0/rublonPam.c -cp /home/vagrant/Rublon-Linux/SSH/PAM/.config /home/vagrant/Rublon-Linux/SSH/RPM/rublonPam-1.0/.config - -#zip rublonPam source -tar cvzf rublonPam.tar.gz rublonPam-1.0 -#move source and spec file to default rpm package creator -cp rublonPam.tar.gz /home/vagrant/rpmbuild/SOURCES/ -cp rublonPam.spec /home/vagrant/rpmbuild/SPECS/ - -#build rpm package in /home/vagrant/rpmbuild/RPMS -#rpmbuild -v -bb /home/vagrant/rpmbuild/SPECS/rublonPam.spec -rpmbuild -ba /home/vagrant/rpmbuild/SPECS/rublonPam.spec -cp /home/vagrant/rpmbuild/RPMS/x86_64/rublonPam-1.0-1.x86_64.rpm /home/vagrant/Rublon-Linux/SSH/RPM diff --git a/SSH/RPM/login_rublon.te b/SSH/RPM/login_rublon.te deleted file mode 100644 index 98e5a61..0000000 --- a/SSH/RPM/login_rublon.te +++ /dev/null @@ -1,8 +0,0 @@ -module login_rublon 1.0; -require { -type http_port_t; -type http_cache_port_t; -type sshd_t; -class tcp_socket name_connect; -}; -#allow sshd_t {http_port_t http_cache_port_t}:tcp_socket name_connect; diff --git a/SSH/RPM/rublonPam-1.0-1.x86_64.rpm b/SSH/RPM/rublonPam-1.0-1.x86_64.rpm deleted file mode 100644 index 9decd697750911a5343d051e7db968a36341809f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27284 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 z 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 diff --git a/SSH/RPM/rublonPam-1.0/.config b/SSH/RPM/rublonPam-1.0/.config deleted file mode 100644 index 4ede779..0000000 --- a/SSH/RPM/rublonPam-1.0/.config +++ /dev/null @@ -1,4 +0,0 @@ -systemToken= -secretKey= -userDomain= -rublonApiServer=https://core.rublon.net diff --git a/SSH/RPM/rublonPam-1.0/Makefile b/SSH/RPM/rublonPam-1.0/Makefile deleted file mode 100644 index a660a5c..0000000 --- a/SSH/RPM/rublonPam-1.0/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -rublonPam: - sudo gcc -fPIC -fno-stack-protector -std=c99 -c rublonPam.c src/pamApp.c src/coreHandler.c src/signatureWrapper.c lib/cfg_parse.c lib/cJSON.c lib/qrcodegen.c - sudo ld -x --shared -o rublonPam.so -lcurl rublonPam.o pamApp.o coreHandler.o signatureWrapper.o cfg_parse.o cJSON.o qrcodegen.o -clean: - sudo rm /lib64/security/rublonPam.so -install: - sudo cp .config /lib64/security - sudo ld -x --shared -o /lib64/security/rublonPam.so -lcurl rublonPam.o pamApp.o coreHandler.o signatureWrapper.o cfg_parse.o cJSON.o qrcodegen.o -# sudo rm rublonPam.o pamApp.o coreHandler.o signatureWrapper.o cfg_parse.o cJSON.o qrcodegen.o -# sudo checkmodule -M -m -o login_rublon.mod login_rublon.te -# sudo semodule_package -o login_rublon.pp -m login_rublon.mod -# sudo semodule -i login_rublon.pp - - diff --git a/SSH/RPM/rublonPam-1.0/lib/cJSON.c b/SSH/RPM/rublonPam-1.0/lib/cJSON.c deleted file mode 100644 index 77aea0c..0000000 --- a/SSH/RPM/rublonPam-1.0/lib/cJSON.c +++ /dev/null @@ -1,2933 +0,0 @@ -/* - Copyright (c) 2009-2017 Dave Gamble and cJSON contributors - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -/* cJSON */ -/* JSON parser in C. */ - -/* disable warnings about old C89 functions in MSVC */ -#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) -#define _CRT_SECURE_NO_DEPRECATE -#endif - -#ifdef __GNUC__ -#pragma GCC visibility push(default) -#endif -#if defined(_MSC_VER) -#pragma warning (push) -/* disable warning about single line comments in system headers */ -#pragma warning (disable : 4001) -#endif - -#include -#include -#include -#include -#include -#include - -#ifdef ENABLE_LOCALES -#include -#endif - -#if defined(_MSC_VER) -#pragma warning (pop) -#endif -#ifdef __GNUC__ -#pragma GCC visibility pop -#endif - -#include "cJSON.h" - -/* define our own boolean type */ -#define true ((cJSON_bool)1) -#define false ((cJSON_bool)0) - -typedef struct { - const unsigned char *json; - size_t position; -} error; -static error global_error = { NULL, 0 }; - -CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) -{ - return (const char*) (global_error.json + global_error.position); -} - -CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) { - if (!cJSON_IsString(item)) { - return NULL; - } - - return item->valuestring; -} - -/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ -#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 10) - #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. -#endif - -CJSON_PUBLIC(const char*) cJSON_Version(void) -{ - static char version[15]; - sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); - - return version; -} - -/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ -static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) -{ - if ((string1 == NULL) || (string2 == NULL)) - { - return 1; - } - - if (string1 == string2) - { - return 0; - } - - for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) - { - if (*string1 == '\0') - { - return 0; - } - } - - return tolower(*string1) - tolower(*string2); -} - -typedef struct internal_hooks -{ - void *(CJSON_CDECL *allocate)(size_t size); - void (CJSON_CDECL *deallocate)(void *pointer); - void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); -} internal_hooks; - -#if defined(_MSC_VER) -/* work around MSVC error C2322: '...' address of dillimport '...' is not static */ -static void * CJSON_CDECL internal_malloc(size_t size) -{ - return malloc(size); -} -static void CJSON_CDECL internal_free(void *pointer) -{ - free(pointer); -} -static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) -{ - return realloc(pointer, size); -} -#else -#define internal_malloc malloc -#define internal_free free -#define internal_realloc realloc -#endif - -static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; - -static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) -{ - size_t length = 0; - unsigned char *copy = NULL; - - if (string == NULL) - { - return NULL; - } - - length = strlen((const char*)string) + sizeof(""); - copy = (unsigned char*)hooks->allocate(length); - if (copy == NULL) - { - return NULL; - } - memcpy(copy, string, length); - - return copy; -} - -CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) -{ - if (hooks == NULL) - { - /* Reset hooks */ - global_hooks.allocate = malloc; - global_hooks.deallocate = free; - global_hooks.reallocate = realloc; - return; - } - - global_hooks.allocate = malloc; - if (hooks->malloc_fn != NULL) - { - global_hooks.allocate = hooks->malloc_fn; - } - - global_hooks.deallocate = free; - if (hooks->free_fn != NULL) - { - global_hooks.deallocate = hooks->free_fn; - } - - /* use realloc only if both free and malloc are used */ - global_hooks.reallocate = NULL; - if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) - { - global_hooks.reallocate = realloc; - } -} - -/* Internal constructor. */ -static cJSON *cJSON_New_Item(const internal_hooks * const hooks) -{ - cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); - if (node) - { - memset(node, '\0', sizeof(cJSON)); - } - - return node; -} - -/* Delete a cJSON structure. */ -CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) -{ - cJSON *next = NULL; - while (item != NULL) - { - next = item->next; - if (!(item->type & cJSON_IsReference) && (item->child != NULL)) - { - cJSON_Delete(item->child); - } - if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) - { - global_hooks.deallocate(item->valuestring); - } - if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) - { - global_hooks.deallocate(item->string); - } - global_hooks.deallocate(item); - item = next; - } -} - -/* get the decimal point character of the current locale */ -static unsigned char get_decimal_point(void) -{ -#ifdef ENABLE_LOCALES - struct lconv *lconv = localeconv(); - return (unsigned char) lconv->decimal_point[0]; -#else - return '.'; -#endif -} - -typedef struct -{ - const unsigned char *content; - size_t length; - size_t offset; - size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ - internal_hooks hooks; -} parse_buffer; - -/* check if the given size is left to read in a given parse buffer (starting with 1) */ -#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) -/* check if the buffer can be accessed at the given index (starting with 0) */ -#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) -#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) -/* get a pointer to the buffer at the position */ -#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) - -/* Parse the input text to generate a number, and populate the result into item. */ -static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) -{ - double number = 0; - unsigned char *after_end = NULL; - unsigned char number_c_string[64]; - unsigned char decimal_point = get_decimal_point(); - size_t i = 0; - - if ((input_buffer == NULL) || (input_buffer->content == NULL)) - { - return false; - } - - /* copy the number into a temporary buffer and replace '.' with the decimal point - * of the current locale (for strtod) - * This also takes care of '\0' not necessarily being available for marking the end of the input */ - for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) - { - switch (buffer_at_offset(input_buffer)[i]) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '+': - case '-': - case 'e': - case 'E': - number_c_string[i] = buffer_at_offset(input_buffer)[i]; - break; - - case '.': - number_c_string[i] = decimal_point; - break; - - default: - goto loop_end; - } - } -loop_end: - number_c_string[i] = '\0'; - - number = strtod((const char*)number_c_string, (char**)&after_end); - if (number_c_string == after_end) - { - return false; /* parse_error */ - } - - item->valuedouble = number; - - /* use saturation in case of overflow */ - if (number >= INT_MAX) - { - item->valueint = INT_MAX; - } - else if (number <= (double)INT_MIN) - { - item->valueint = INT_MIN; - } - else - { - item->valueint = (int)number; - } - - item->type = cJSON_Number; - - input_buffer->offset += (size_t)(after_end - number_c_string); - return true; -} - -/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ -CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) -{ - if (number >= INT_MAX) - { - object->valueint = INT_MAX; - } - else if (number <= (double)INT_MIN) - { - object->valueint = INT_MIN; - } - else - { - object->valueint = (int)number; - } - - return object->valuedouble = number; -} - -typedef struct -{ - unsigned char *buffer; - size_t length; - size_t offset; - size_t depth; /* current nesting depth (for formatted printing) */ - cJSON_bool noalloc; - cJSON_bool format; /* is this print a formatted print */ - internal_hooks hooks; -} printbuffer; - -/* realloc printbuffer if necessary to have at least "needed" bytes more */ -static unsigned char* ensure(printbuffer * const p, size_t needed) -{ - unsigned char *newbuffer = NULL; - size_t newsize = 0; - - if ((p == NULL) || (p->buffer == NULL)) - { - return NULL; - } - - if ((p->length > 0) && (p->offset >= p->length)) - { - /* make sure that offset is valid */ - return NULL; - } - - if (needed > INT_MAX) - { - /* sizes bigger than INT_MAX are currently not supported */ - return NULL; - } - - needed += p->offset + 1; - if (needed <= p->length) - { - return p->buffer + p->offset; - } - - if (p->noalloc) { - return NULL; - } - - /* calculate new buffer size */ - if (needed > (INT_MAX / 2)) - { - /* overflow of int, use INT_MAX if possible */ - if (needed <= INT_MAX) - { - newsize = INT_MAX; - } - else - { - return NULL; - } - } - else - { - newsize = needed * 2; - } - - if (p->hooks.reallocate != NULL) - { - /* reallocate with realloc if available */ - newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); - if (newbuffer == NULL) - { - p->hooks.deallocate(p->buffer); - p->length = 0; - p->buffer = NULL; - - return NULL; - } - } - else - { - /* otherwise reallocate manually */ - newbuffer = (unsigned char*)p->hooks.allocate(newsize); - if (!newbuffer) - { - p->hooks.deallocate(p->buffer); - p->length = 0; - p->buffer = NULL; - - return NULL; - } - if (newbuffer) - { - memcpy(newbuffer, p->buffer, p->offset + 1); - } - p->hooks.deallocate(p->buffer); - } - p->length = newsize; - p->buffer = newbuffer; - - return newbuffer + p->offset; -} - -/* calculate the new length of the string in a printbuffer and update the offset */ -static void update_offset(printbuffer * const buffer) -{ - const unsigned char *buffer_pointer = NULL; - if ((buffer == NULL) || (buffer->buffer == NULL)) - { - return; - } - buffer_pointer = buffer->buffer + buffer->offset; - - buffer->offset += strlen((const char*)buffer_pointer); -} - -/* Render the number nicely from the given item into a string. */ -static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output_pointer = NULL; - double d = item->valuedouble; - int length = 0; - size_t i = 0; - unsigned char number_buffer[26]; /* temporary buffer to print the number into */ - unsigned char decimal_point = get_decimal_point(); - double test; - - if (output_buffer == NULL) - { - return false; - } - - /* This checks for NaN and Infinity */ - if ((d * 0) != 0) - { - length = sprintf((char*)number_buffer, "null"); - } - else - { - /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ - length = sprintf((char*)number_buffer, "%1.15g", d); - - /* Check whether the original double can be recovered */ - if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) - { - /* If not, print with 17 decimal places of precision */ - length = sprintf((char*)number_buffer, "%1.17g", d); - } - } - - /* sprintf failed or buffer overrun occured */ - if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) - { - return false; - } - - /* reserve appropriate space in the output */ - output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); - if (output_pointer == NULL) - { - return false; - } - - /* copy the printed number to the output and replace locale - * dependent decimal point with '.' */ - for (i = 0; i < ((size_t)length); i++) - { - if (number_buffer[i] == decimal_point) - { - output_pointer[i] = '.'; - continue; - } - - output_pointer[i] = number_buffer[i]; - } - output_pointer[i] = '\0'; - - output_buffer->offset += (size_t)length; - - return true; -} - -/* parse 4 digit hexadecimal number */ -static unsigned parse_hex4(const unsigned char * const input) -{ - unsigned int h = 0; - size_t i = 0; - - for (i = 0; i < 4; i++) - { - /* parse digit */ - if ((input[i] >= '0') && (input[i] <= '9')) - { - h += (unsigned int) input[i] - '0'; - } - else if ((input[i] >= 'A') && (input[i] <= 'F')) - { - h += (unsigned int) 10 + input[i] - 'A'; - } - else if ((input[i] >= 'a') && (input[i] <= 'f')) - { - h += (unsigned int) 10 + input[i] - 'a'; - } - else /* invalid */ - { - return 0; - } - - if (i < 3) - { - /* shift left to make place for the next nibble */ - h = h << 4; - } - } - - return h; -} - -/* converts a UTF-16 literal to UTF-8 - * A literal can be one or two sequences of the form \uXXXX */ -static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) -{ - long unsigned int codepoint = 0; - unsigned int first_code = 0; - const unsigned char *first_sequence = input_pointer; - unsigned char utf8_length = 0; - unsigned char utf8_position = 0; - unsigned char sequence_length = 0; - unsigned char first_byte_mark = 0; - - if ((input_end - first_sequence) < 6) - { - /* input ends unexpectedly */ - goto fail; - } - - /* get the first utf16 sequence */ - first_code = parse_hex4(first_sequence + 2); - - /* check that the code is valid */ - if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) - { - goto fail; - } - - /* UTF16 surrogate pair */ - if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) - { - const unsigned char *second_sequence = first_sequence + 6; - unsigned int second_code = 0; - sequence_length = 12; /* \uXXXX\uXXXX */ - - if ((input_end - second_sequence) < 6) - { - /* input ends unexpectedly */ - goto fail; - } - - if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) - { - /* missing second half of the surrogate pair */ - goto fail; - } - - /* get the second utf16 sequence */ - second_code = parse_hex4(second_sequence + 2); - /* check that the code is valid */ - if ((second_code < 0xDC00) || (second_code > 0xDFFF)) - { - /* invalid second half of the surrogate pair */ - goto fail; - } - - - /* calculate the unicode codepoint from the surrogate pair */ - codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); - } - else - { - sequence_length = 6; /* \uXXXX */ - codepoint = first_code; - } - - /* encode as UTF-8 - * takes at maximum 4 bytes to encode: - * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ - if (codepoint < 0x80) - { - /* normal ascii, encoding 0xxxxxxx */ - utf8_length = 1; - } - else if (codepoint < 0x800) - { - /* two bytes, encoding 110xxxxx 10xxxxxx */ - utf8_length = 2; - first_byte_mark = 0xC0; /* 11000000 */ - } - else if (codepoint < 0x10000) - { - /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ - utf8_length = 3; - first_byte_mark = 0xE0; /* 11100000 */ - } - else if (codepoint <= 0x10FFFF) - { - /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ - utf8_length = 4; - first_byte_mark = 0xF0; /* 11110000 */ - } - else - { - /* invalid unicode codepoint */ - goto fail; - } - - /* encode as utf8 */ - for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) - { - /* 10xxxxxx */ - (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); - codepoint >>= 6; - } - /* encode first byte */ - if (utf8_length > 1) - { - (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); - } - else - { - (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); - } - - *output_pointer += utf8_length; - - return sequence_length; - -fail: - return 0; -} - -/* Parse the input text into an unescaped cinput, and populate item. */ -static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) -{ - const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; - const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; - unsigned char *output_pointer = NULL; - unsigned char *output = NULL; - - /* not a string */ - if (buffer_at_offset(input_buffer)[0] != '\"') - { - goto fail; - } - - { - /* calculate approximate size of the output (overestimate) */ - size_t allocation_length = 0; - size_t skipped_bytes = 0; - while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) - { - /* is escape sequence */ - if (input_end[0] == '\\') - { - if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) - { - /* prevent buffer overflow when last input character is a backslash */ - goto fail; - } - skipped_bytes++; - input_end++; - } - input_end++; - } - if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) - { - goto fail; /* string ended unexpectedly */ - } - - /* This is at most how much we need for the output */ - allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; - output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); - if (output == NULL) - { - goto fail; /* allocation failure */ - } - } - - output_pointer = output; - /* loop through the string literal */ - while (input_pointer < input_end) - { - if (*input_pointer != '\\') - { - *output_pointer++ = *input_pointer++; - } - /* escape sequence */ - else - { - unsigned char sequence_length = 2; - if ((input_end - input_pointer) < 1) - { - goto fail; - } - - switch (input_pointer[1]) - { - case 'b': - *output_pointer++ = '\b'; - break; - case 'f': - *output_pointer++ = '\f'; - break; - case 'n': - *output_pointer++ = '\n'; - break; - case 'r': - *output_pointer++ = '\r'; - break; - case 't': - *output_pointer++ = '\t'; - break; - case '\"': - case '\\': - case '/': - *output_pointer++ = input_pointer[1]; - break; - - /* UTF-16 literal */ - case 'u': - sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); - if (sequence_length == 0) - { - /* failed to convert UTF16-literal to UTF-8 */ - goto fail; - } - break; - - default: - goto fail; - } - input_pointer += sequence_length; - } - } - - /* zero terminate the output */ - *output_pointer = '\0'; - - item->type = cJSON_String; - item->valuestring = (char*)output; - - input_buffer->offset = (size_t) (input_end - input_buffer->content); - input_buffer->offset++; - - return true; - -fail: - if (output != NULL) - { - input_buffer->hooks.deallocate(output); - } - - if (input_pointer != NULL) - { - input_buffer->offset = (size_t)(input_pointer - input_buffer->content); - } - - return false; -} - -/* Render the cstring provided to an escaped version that can be printed. */ -static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) -{ - const unsigned char *input_pointer = NULL; - unsigned char *output = NULL; - unsigned char *output_pointer = NULL; - size_t output_length = 0; - /* numbers of additional characters needed for escaping */ - size_t escape_characters = 0; - - if (output_buffer == NULL) - { - return false; - } - - /* empty string */ - if (input == NULL) - { - output = ensure(output_buffer, sizeof("\"\"")); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "\"\""); - - return true; - } - - /* set "flag" to 1 if something needs to be escaped */ - for (input_pointer = input; *input_pointer; input_pointer++) - { - switch (*input_pointer) - { - case '\"': - case '\\': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - /* one character escape sequence */ - escape_characters++; - break; - default: - if (*input_pointer < 32) - { - /* UTF-16 escape sequence uXXXX */ - escape_characters += 5; - } - break; - } - } - output_length = (size_t)(input_pointer - input) + escape_characters; - - output = ensure(output_buffer, output_length + sizeof("\"\"")); - if (output == NULL) - { - return false; - } - - /* no characters have to be escaped */ - if (escape_characters == 0) - { - output[0] = '\"'; - memcpy(output + 1, input, output_length); - output[output_length + 1] = '\"'; - output[output_length + 2] = '\0'; - - return true; - } - - output[0] = '\"'; - output_pointer = output + 1; - /* copy the string */ - for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) - { - if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) - { - /* normal character, copy */ - *output_pointer = *input_pointer; - } - else - { - /* character needs to be escaped */ - *output_pointer++ = '\\'; - switch (*input_pointer) - { - case '\\': - *output_pointer = '\\'; - break; - case '\"': - *output_pointer = '\"'; - break; - case '\b': - *output_pointer = 'b'; - break; - case '\f': - *output_pointer = 'f'; - break; - case '\n': - *output_pointer = 'n'; - break; - case '\r': - *output_pointer = 'r'; - break; - case '\t': - *output_pointer = 't'; - break; - default: - /* escape and print as unicode codepoint */ - sprintf((char*)output_pointer, "u%04x", *input_pointer); - output_pointer += 4; - break; - } - } - } - output[output_length + 1] = '\"'; - output[output_length + 2] = '\0'; - - return true; -} - -/* Invoke print_string_ptr (which is useful) on an item. */ -static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) -{ - return print_string_ptr((unsigned char*)item->valuestring, p); -} - -/* Predeclare these prototypes. */ -static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); -static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); -static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); -static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); -static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); -static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); - -/* Utility to jump whitespace and cr/lf */ -static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) -{ - if ((buffer == NULL) || (buffer->content == NULL)) - { - return NULL; - } - - while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) - { - buffer->offset++; - } - - if (buffer->offset == buffer->length) - { - buffer->offset--; - } - - return buffer; -} - -/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ -static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) -{ - if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) - { - return NULL; - } - - if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) - { - buffer->offset += 3; - } - - return buffer; -} - -/* Parse an object - create a new root, and populate. */ -CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) -{ - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; - cJSON *item = NULL; - - /* reset error position */ - global_error.json = NULL; - global_error.position = 0; - - if (value == NULL) - { - goto fail; - } - - buffer.content = (const unsigned char*)value; - buffer.length = strlen((const char*)value) + sizeof(""); - buffer.offset = 0; - buffer.hooks = global_hooks; - - item = cJSON_New_Item(&global_hooks); - if (item == NULL) /* memory fail */ - { - goto fail; - } - - if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) - { - /* parse failure. ep is set. */ - goto fail; - } - - /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ - if (require_null_terminated) - { - buffer_skip_whitespace(&buffer); - if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') - { - goto fail; - } - } - if (return_parse_end) - { - *return_parse_end = (const char*)buffer_at_offset(&buffer); - } - - return item; - -fail: - if (item != NULL) - { - cJSON_Delete(item); - } - - if (value != NULL) - { - error local_error; - local_error.json = (const unsigned char*)value; - local_error.position = 0; - - if (buffer.offset < buffer.length) - { - local_error.position = buffer.offset; - } - else if (buffer.length > 0) - { - local_error.position = buffer.length - 1; - } - - if (return_parse_end != NULL) - { - *return_parse_end = (const char*)local_error.json + local_error.position; - } - - global_error = local_error; - } - - return NULL; -} - -/* Default options for cJSON_Parse */ -CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) -{ - return cJSON_ParseWithOpts(value, 0, 0); -} - -#define cjson_min(a, b) ((a < b) ? a : b) - -static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) -{ - static const size_t default_buffer_size = 256; - printbuffer buffer[1]; - unsigned char *printed = NULL; - - memset(buffer, 0, sizeof(buffer)); - - /* create buffer */ - buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); - buffer->length = default_buffer_size; - buffer->format = format; - buffer->hooks = *hooks; - if (buffer->buffer == NULL) - { - goto fail; - } - - /* print the value */ - if (!print_value(item, buffer)) - { - goto fail; - } - update_offset(buffer); - - /* check if reallocate is available */ - if (hooks->reallocate != NULL) - { - printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); - if (printed == NULL) { - goto fail; - } - buffer->buffer = NULL; - } - else /* otherwise copy the JSON over to a new buffer */ - { - printed = (unsigned char*) hooks->allocate(buffer->offset + 1); - if (printed == NULL) - { - goto fail; - } - memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); - printed[buffer->offset] = '\0'; /* just to be sure */ - - /* free the buffer */ - hooks->deallocate(buffer->buffer); - } - - return printed; - -fail: - if (buffer->buffer != NULL) - { - hooks->deallocate(buffer->buffer); - } - - if (printed != NULL) - { - hooks->deallocate(printed); - } - - return NULL; -} - -/* Render a cJSON item/entity/structure to text. */ -CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) -{ - return (char*)print(item, true, &global_hooks); -} - -CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) -{ - return (char*)print(item, false, &global_hooks); -} - -CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) -{ - printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - - if (prebuffer < 0) - { - return NULL; - } - - p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); - if (!p.buffer) - { - return NULL; - } - - p.length = (size_t)prebuffer; - p.offset = 0; - p.noalloc = false; - p.format = fmt; - p.hooks = global_hooks; - - if (!print_value(item, &p)) - { - global_hooks.deallocate(p.buffer); - return NULL; - } - - return (char*)p.buffer; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) -{ - printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - - if ((len < 0) || (buf == NULL)) - { - return false; - } - - p.buffer = (unsigned char*)buf; - p.length = (size_t)len; - p.offset = 0; - p.noalloc = true; - p.format = fmt; - p.hooks = global_hooks; - - return print_value(item, &p); -} - -/* Parser core - when encountering text, process appropriately. */ -static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) -{ - if ((input_buffer == NULL) || (input_buffer->content == NULL)) - { - return false; /* no input */ - } - - /* parse the different types of values */ - /* null */ - if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) - { - item->type = cJSON_NULL; - input_buffer->offset += 4; - return true; - } - /* false */ - if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) - { - item->type = cJSON_False; - input_buffer->offset += 5; - return true; - } - /* true */ - if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) - { - item->type = cJSON_True; - item->valueint = 1; - input_buffer->offset += 4; - return true; - } - /* string */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) - { - return parse_string(item, input_buffer); - } - /* number */ - if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) - { - return parse_number(item, input_buffer); - } - /* array */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) - { - return parse_array(item, input_buffer); - } - /* object */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) - { - return parse_object(item, input_buffer); - } - - return false; -} - -/* Render a value to text. */ -static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output = NULL; - - if ((item == NULL) || (output_buffer == NULL)) - { - return false; - } - - switch ((item->type) & 0xFF) - { - case cJSON_NULL: - output = ensure(output_buffer, 5); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "null"); - return true; - - case cJSON_False: - output = ensure(output_buffer, 6); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "false"); - return true; - - case cJSON_True: - output = ensure(output_buffer, 5); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "true"); - return true; - - case cJSON_Number: - return print_number(item, output_buffer); - - case cJSON_Raw: - { - size_t raw_length = 0; - if (item->valuestring == NULL) - { - return false; - } - - raw_length = strlen(item->valuestring) + sizeof(""); - output = ensure(output_buffer, raw_length); - if (output == NULL) - { - return false; - } - memcpy(output, item->valuestring, raw_length); - return true; - } - - case cJSON_String: - return print_string(item, output_buffer); - - case cJSON_Array: - return print_array(item, output_buffer); - - case cJSON_Object: - return print_object(item, output_buffer); - - default: - return false; - } -} - -/* Build an array from input text. */ -static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) -{ - cJSON *head = NULL; /* head of the linked list */ - cJSON *current_item = NULL; - - if (input_buffer->depth >= CJSON_NESTING_LIMIT) - { - return false; /* to deeply nested */ - } - input_buffer->depth++; - - if (buffer_at_offset(input_buffer)[0] != '[') - { - /* not an array */ - goto fail; - } - - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) - { - /* empty array */ - goto success; - } - - /* check if we skipped to the end of the buffer */ - if (cannot_access_at_index(input_buffer, 0)) - { - input_buffer->offset--; - goto fail; - } - - /* step back to character in front of the first element */ - input_buffer->offset--; - /* loop through the comma separated array elements */ - do - { - /* allocate next item */ - cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); - if (new_item == NULL) - { - goto fail; /* allocation failure */ - } - - /* attach next item to list */ - if (head == NULL) - { - /* start the linked list */ - current_item = head = new_item; - } - else - { - /* add to the end and advance */ - current_item->next = new_item; - new_item->prev = current_item; - current_item = new_item; - } - - /* parse next value */ - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (!parse_value(current_item, input_buffer)) - { - goto fail; /* failed to parse value */ - } - buffer_skip_whitespace(input_buffer); - } - while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); - - if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') - { - goto fail; /* expected end of array */ - } - -success: - input_buffer->depth--; - - item->type = cJSON_Array; - item->child = head; - - input_buffer->offset++; - - return true; - -fail: - if (head != NULL) - { - cJSON_Delete(head); - } - - return false; -} - -/* Render an array to text */ -static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output_pointer = NULL; - size_t length = 0; - cJSON *current_element = item->child; - - if (output_buffer == NULL) - { - return false; - } - - /* Compose the output array. */ - /* opening square bracket */ - output_pointer = ensure(output_buffer, 1); - if (output_pointer == NULL) - { - return false; - } - - *output_pointer = '['; - output_buffer->offset++; - output_buffer->depth++; - - while (current_element != NULL) - { - if (!print_value(current_element, output_buffer)) - { - return false; - } - update_offset(output_buffer); - if (current_element->next) - { - length = (size_t) (output_buffer->format ? 2 : 1); - output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) - { - return false; - } - *output_pointer++ = ','; - if(output_buffer->format) - { - *output_pointer++ = ' '; - } - *output_pointer = '\0'; - output_buffer->offset += length; - } - current_element = current_element->next; - } - - output_pointer = ensure(output_buffer, 2); - if (output_pointer == NULL) - { - return false; - } - *output_pointer++ = ']'; - *output_pointer = '\0'; - output_buffer->depth--; - - return true; -} - -/* Build an object from the text. */ -static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) -{ - cJSON *head = NULL; /* linked list head */ - cJSON *current_item = NULL; - - if (input_buffer->depth >= CJSON_NESTING_LIMIT) - { - return false; /* to deeply nested */ - } - input_buffer->depth++; - - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) - { - goto fail; /* not an object */ - } - - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) - { - goto success; /* empty object */ - } - - /* check if we skipped to the end of the buffer */ - if (cannot_access_at_index(input_buffer, 0)) - { - input_buffer->offset--; - goto fail; - } - - /* step back to character in front of the first element */ - input_buffer->offset--; - /* loop through the comma separated array elements */ - do - { - /* allocate next item */ - cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); - if (new_item == NULL) - { - goto fail; /* allocation failure */ - } - - /* attach next item to list */ - if (head == NULL) - { - /* start the linked list */ - current_item = head = new_item; - } - else - { - /* add to the end and advance */ - current_item->next = new_item; - new_item->prev = current_item; - current_item = new_item; - } - - /* parse the name of the child */ - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (!parse_string(current_item, input_buffer)) - { - goto fail; /* faile to parse name */ - } - buffer_skip_whitespace(input_buffer); - - /* swap valuestring and string, because we parsed the name */ - current_item->string = current_item->valuestring; - current_item->valuestring = NULL; - - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) - { - goto fail; /* invalid object */ - } - - /* parse the value */ - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (!parse_value(current_item, input_buffer)) - { - goto fail; /* failed to parse value */ - } - buffer_skip_whitespace(input_buffer); - } - while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); - - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) - { - goto fail; /* expected end of object */ - } - -success: - input_buffer->depth--; - - item->type = cJSON_Object; - item->child = head; - - input_buffer->offset++; - return true; - -fail: - if (head != NULL) - { - cJSON_Delete(head); - } - - return false; -} - -/* Render an object to text. */ -static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output_pointer = NULL; - size_t length = 0; - cJSON *current_item = item->child; - - if (output_buffer == NULL) - { - return false; - } - - /* Compose the output: */ - length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ - output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) - { - return false; - } - - *output_pointer++ = '{'; - output_buffer->depth++; - if (output_buffer->format) - { - *output_pointer++ = '\n'; - } - output_buffer->offset += length; - - while (current_item) - { - if (output_buffer->format) - { - size_t i; - output_pointer = ensure(output_buffer, output_buffer->depth); - if (output_pointer == NULL) - { - return false; - } - for (i = 0; i < output_buffer->depth; i++) - { - *output_pointer++ = '\t'; - } - output_buffer->offset += output_buffer->depth; - } - - /* print key */ - if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) - { - return false; - } - update_offset(output_buffer); - - length = (size_t) (output_buffer->format ? 2 : 1); - output_pointer = ensure(output_buffer, length); - if (output_pointer == NULL) - { - return false; - } - *output_pointer++ = ':'; - if (output_buffer->format) - { - *output_pointer++ = '\t'; - } - output_buffer->offset += length; - - /* print value */ - if (!print_value(current_item, output_buffer)) - { - return false; - } - update_offset(output_buffer); - - /* print comma if not last */ - length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); - output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) - { - return false; - } - if (current_item->next) - { - *output_pointer++ = ','; - } - - if (output_buffer->format) - { - *output_pointer++ = '\n'; - } - *output_pointer = '\0'; - output_buffer->offset += length; - - current_item = current_item->next; - } - - output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); - if (output_pointer == NULL) - { - return false; - } - if (output_buffer->format) - { - size_t i; - for (i = 0; i < (output_buffer->depth - 1); i++) - { - *output_pointer++ = '\t'; - } - } - *output_pointer++ = '}'; - *output_pointer = '\0'; - output_buffer->depth--; - - return true; -} - -/* Get Array size/item / object item. */ -CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) -{ - cJSON *child = NULL; - size_t size = 0; - - if (array == NULL) - { - return 0; - } - - child = array->child; - - while(child != NULL) - { - size++; - child = child->next; - } - - /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ - - return (int)size; -} - -static cJSON* get_array_item(const cJSON *array, size_t index) -{ - cJSON *current_child = NULL; - - if (array == NULL) - { - return NULL; - } - - current_child = array->child; - while ((current_child != NULL) && (index > 0)) - { - index--; - current_child = current_child->next; - } - - return current_child; -} - -CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) -{ - if (index < 0) - { - return NULL; - } - - return get_array_item(array, (size_t)index); -} - -static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) -{ - cJSON *current_element = NULL; - - if ((object == NULL) || (name == NULL)) - { - return NULL; - } - - current_element = object->child; - if (case_sensitive) - { - while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) - { - current_element = current_element->next; - } - } - else - { - while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) - { - current_element = current_element->next; - } - } - - if ((current_element == NULL) || (current_element->string == NULL)) { - return NULL; - } - - return current_element; -} - -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) -{ - return get_object_item(object, string, false); -} - -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) -{ - return get_object_item(object, string, true); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) -{ - return cJSON_GetObjectItem(object, string) ? 1 : 0; -} - -/* Utility for array list handling. */ -static void suffix_object(cJSON *prev, cJSON *item) -{ - prev->next = item; - item->prev = prev; -} - -/* Utility for handling references. */ -static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) -{ - cJSON *reference = NULL; - if (item == NULL) - { - return NULL; - } - - reference = cJSON_New_Item(hooks); - if (reference == NULL) - { - return NULL; - } - - memcpy(reference, item, sizeof(cJSON)); - reference->string = NULL; - reference->type |= cJSON_IsReference; - reference->next = reference->prev = NULL; - return reference; -} - -static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) -{ - cJSON *child = NULL; - - if ((item == NULL) || (array == NULL)) - { - return false; - } - - child = array->child; - - if (child == NULL) - { - /* list is empty, start new one */ - array->child = item; - } - else - { - /* append to the end */ - while (child->next) - { - child = child->next; - } - suffix_object(child, item); - } - - return true; -} - -/* Add item to array/object. */ -CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) -{ - add_item_to_array(array, item); -} - -#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) - #pragma GCC diagnostic push -#endif -#ifdef __GNUC__ -#pragma GCC diagnostic ignored "-Wcast-qual" -#endif -/* helper function to cast away const */ -static void* cast_away_const(const void* string) -{ - return (void*)string; -} -#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) - #pragma GCC diagnostic pop -#endif - - -static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) -{ - char *new_key = NULL; - int new_type = cJSON_Invalid; - - if ((object == NULL) || (string == NULL) || (item == NULL)) - { - return false; - } - - if (constant_key) - { - new_key = (char*)cast_away_const(string); - new_type = item->type | cJSON_StringIsConst; - } - else - { - new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); - if (new_key == NULL) - { - return false; - } - - new_type = item->type & ~cJSON_StringIsConst; - } - - if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) - { - hooks->deallocate(item->string); - } - - item->string = new_key; - item->type = new_type; - - return add_item_to_array(object, item); -} - -CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) -{ - add_item_to_object(object, string, item, &global_hooks, false); -} - -/* Add an item to an object with constant string as key */ -CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) -{ - add_item_to_object(object, string, item, &global_hooks, true); -} - -CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) -{ - if (array == NULL) - { - return; - } - - add_item_to_array(array, create_reference(item, &global_hooks)); -} - -CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) -{ - if ((object == NULL) || (string == NULL)) - { - return; - } - - add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); -} - -CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) -{ - cJSON *null = cJSON_CreateNull(); - if (add_item_to_object(object, name, null, &global_hooks, false)) - { - return null; - } - - cJSON_Delete(null); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) -{ - cJSON *true_item = cJSON_CreateTrue(); - if (add_item_to_object(object, name, true_item, &global_hooks, false)) - { - return true_item; - } - - cJSON_Delete(true_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) -{ - cJSON *false_item = cJSON_CreateFalse(); - if (add_item_to_object(object, name, false_item, &global_hooks, false)) - { - return false_item; - } - - cJSON_Delete(false_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) -{ - cJSON *bool_item = cJSON_CreateBool(boolean); - if (add_item_to_object(object, name, bool_item, &global_hooks, false)) - { - return bool_item; - } - - cJSON_Delete(bool_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) -{ - cJSON *number_item = cJSON_CreateNumber(number); - if (add_item_to_object(object, name, number_item, &global_hooks, false)) - { - return number_item; - } - - cJSON_Delete(number_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) -{ - cJSON *string_item = cJSON_CreateString(string); - if (add_item_to_object(object, name, string_item, &global_hooks, false)) - { - return string_item; - } - - cJSON_Delete(string_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) -{ - cJSON *raw_item = cJSON_CreateRaw(raw); - if (add_item_to_object(object, name, raw_item, &global_hooks, false)) - { - return raw_item; - } - - cJSON_Delete(raw_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) -{ - cJSON *object_item = cJSON_CreateObject(); - if (add_item_to_object(object, name, object_item, &global_hooks, false)) - { - return object_item; - } - - cJSON_Delete(object_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) -{ - cJSON *array = cJSON_CreateArray(); - if (add_item_to_object(object, name, array, &global_hooks, false)) - { - return array; - } - - cJSON_Delete(array); - return NULL; -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) -{ - if ((parent == NULL) || (item == NULL)) - { - return NULL; - } - - if (item->prev != NULL) - { - /* not the first element */ - item->prev->next = item->next; - } - if (item->next != NULL) - { - /* not the last element */ - item->next->prev = item->prev; - } - - if (item == parent->child) - { - /* first element */ - parent->child = item->next; - } - /* make sure the detached item doesn't point anywhere anymore */ - item->prev = NULL; - item->next = NULL; - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) -{ - if (which < 0) - { - return NULL; - } - - return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); -} - -CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) -{ - cJSON_Delete(cJSON_DetachItemFromArray(array, which)); -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) -{ - cJSON *to_detach = cJSON_GetObjectItem(object, string); - - return cJSON_DetachItemViaPointer(object, to_detach); -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) -{ - cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); - - return cJSON_DetachItemViaPointer(object, to_detach); -} - -CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) -{ - cJSON_Delete(cJSON_DetachItemFromObject(object, string)); -} - -CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) -{ - cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); -} - -/* Replace array/object items with new ones. */ -CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) -{ - cJSON *after_inserted = NULL; - - if (which < 0) - { - return; - } - - after_inserted = get_array_item(array, (size_t)which); - if (after_inserted == NULL) - { - add_item_to_array(array, newitem); - return; - } - - newitem->next = after_inserted; - newitem->prev = after_inserted->prev; - after_inserted->prev = newitem; - if (after_inserted == array->child) - { - array->child = newitem; - } - else - { - newitem->prev->next = newitem; - } -} - -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) -{ - if ((parent == NULL) || (replacement == NULL) || (item == NULL)) - { - return false; - } - - if (replacement == item) - { - return true; - } - - replacement->next = item->next; - replacement->prev = item->prev; - - if (replacement->next != NULL) - { - replacement->next->prev = replacement; - } - if (replacement->prev != NULL) - { - replacement->prev->next = replacement; - } - if (parent->child == item) - { - parent->child = replacement; - } - - item->next = NULL; - item->prev = NULL; - cJSON_Delete(item); - - return true; -} - -CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) -{ - if (which < 0) - { - return; - } - - cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); -} - -static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) -{ - if ((replacement == NULL) || (string == NULL)) - { - return false; - } - - /* replace the name in the replacement */ - if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) - { - cJSON_free(replacement->string); - } - replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); - replacement->type &= ~cJSON_StringIsConst; - - cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); - - return true; -} - -CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) -{ - replace_item_in_object(object, string, newitem, false); -} - -CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) -{ - replace_item_in_object(object, string, newitem, true); -} - -/* Create basic types: */ -CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_NULL; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_True; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_False; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = b ? cJSON_True : cJSON_False; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_Number; - item->valuedouble = num; - - /* use saturation in case of overflow */ - if (num >= INT_MAX) - { - item->valueint = INT_MAX; - } - else if (num <= (double)INT_MIN) - { - item->valueint = INT_MIN; - } - else - { - item->valueint = (int)num; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_String; - item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); - if(!item->valuestring) - { - cJSON_Delete(item); - return NULL; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) - { - item->type = cJSON_String | cJSON_IsReference; - item->valuestring = (char*)cast_away_const(string); - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) { - item->type = cJSON_Object | cJSON_IsReference; - item->child = (cJSON*)cast_away_const(child); - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) { - item->type = cJSON_Array | cJSON_IsReference; - item->child = (cJSON*)cast_away_const(child); - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_Raw; - item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); - if(!item->valuestring) - { - cJSON_Delete(item); - return NULL; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type=cJSON_Array; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if (item) - { - item->type = cJSON_Object; - } - - return item; -} - -/* Create Arrays: */ -CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - for(i = 0; a && (i < (size_t)count); i++) - { - n = cJSON_CreateNumber(numbers[i]); - if (!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p, n); - } - p = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - - for(i = 0; a && (i < (size_t)count); i++) - { - n = cJSON_CreateNumber((double)numbers[i]); - if(!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p, n); - } - p = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - - for(i = 0;a && (i < (size_t)count); i++) - { - n = cJSON_CreateNumber(numbers[i]); - if(!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p, n); - } - p = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (strings == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - - for (i = 0; a && (i < (size_t)count); i++) - { - n = cJSON_CreateString(strings[i]); - if(!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p,n); - } - p = n; - } - - return a; -} - -/* Duplication */ -CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) -{ - cJSON *newitem = NULL; - cJSON *child = NULL; - cJSON *next = NULL; - cJSON *newchild = NULL; - - /* Bail on bad ptr */ - if (!item) - { - goto fail; - } - /* Create new item */ - newitem = cJSON_New_Item(&global_hooks); - if (!newitem) - { - goto fail; - } - /* Copy over all vars */ - newitem->type = item->type & (~cJSON_IsReference); - newitem->valueint = item->valueint; - newitem->valuedouble = item->valuedouble; - if (item->valuestring) - { - newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); - if (!newitem->valuestring) - { - goto fail; - } - } - if (item->string) - { - newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); - if (!newitem->string) - { - goto fail; - } - } - /* If non-recursive, then we're done! */ - if (!recurse) - { - return newitem; - } - /* Walk the ->next chain for the child. */ - child = item->child; - while (child != NULL) - { - newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ - if (!newchild) - { - goto fail; - } - if (next != NULL) - { - /* If newitem->child already set, then crosswire ->prev and ->next and move on */ - next->next = newchild; - newchild->prev = next; - next = newchild; - } - else - { - /* Set newitem->child and move to it */ - newitem->child = newchild; - next = newchild; - } - child = child->next; - } - - return newitem; - -fail: - if (newitem != NULL) - { - cJSON_Delete(newitem); - } - - return NULL; -} - -CJSON_PUBLIC(void) cJSON_Minify(char *json) -{ - unsigned char *into = (unsigned char*)json; - - if (json == NULL) - { - return; - } - - while (*json) - { - if (*json == ' ') - { - json++; - } - else if (*json == '\t') - { - /* Whitespace characters. */ - json++; - } - else if (*json == '\r') - { - json++; - } - else if (*json=='\n') - { - json++; - } - else if ((*json == '/') && (json[1] == '/')) - { - /* double-slash comments, to end of line. */ - while (*json && (*json != '\n')) - { - json++; - } - } - else if ((*json == '/') && (json[1] == '*')) - { - /* multiline comments. */ - while (*json && !((*json == '*') && (json[1] == '/'))) - { - json++; - } - json += 2; - } - else if (*json == '\"') - { - /* string literals, which are \" sensitive. */ - *into++ = (unsigned char)*json++; - while (*json && (*json != '\"')) - { - if (*json == '\\') - { - *into++ = (unsigned char)*json++; - } - *into++ = (unsigned char)*json++; - } - *into++ = (unsigned char)*json++; - } - else - { - /* All other characters. */ - *into++ = (unsigned char)*json++; - } - } - - /* and null-terminate. */ - *into = '\0'; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Invalid; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_False; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xff) == cJSON_True; -} - - -CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & (cJSON_True | cJSON_False)) != 0; -} -CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_NULL; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Number; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_String; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Array; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Object; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Raw; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) -{ - if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) - { - return false; - } - - /* check if type is valid */ - switch (a->type & 0xFF) - { - case cJSON_False: - case cJSON_True: - case cJSON_NULL: - case cJSON_Number: - case cJSON_String: - case cJSON_Raw: - case cJSON_Array: - case cJSON_Object: - break; - - default: - return false; - } - - /* identical objects are equal */ - if (a == b) - { - return true; - } - - switch (a->type & 0xFF) - { - /* in these cases and equal type is enough */ - case cJSON_False: - case cJSON_True: - case cJSON_NULL: - return true; - - case cJSON_Number: - if (a->valuedouble == b->valuedouble) - { - return true; - } - return false; - - case cJSON_String: - case cJSON_Raw: - if ((a->valuestring == NULL) || (b->valuestring == NULL)) - { - return false; - } - if (strcmp(a->valuestring, b->valuestring) == 0) - { - return true; - } - - return false; - - case cJSON_Array: - { - cJSON *a_element = a->child; - cJSON *b_element = b->child; - - for (; (a_element != NULL) && (b_element != NULL);) - { - if (!cJSON_Compare(a_element, b_element, case_sensitive)) - { - return false; - } - - a_element = a_element->next; - b_element = b_element->next; - } - - /* one of the arrays is longer than the other */ - if (a_element != b_element) { - return false; - } - - return true; - } - - case cJSON_Object: - { - cJSON *a_element = NULL; - cJSON *b_element = NULL; - cJSON_ArrayForEach(a_element, a) - { - /* TODO This has O(n^2) runtime, which is horrible! */ - b_element = get_object_item(b, a_element->string, case_sensitive); - if (b_element == NULL) - { - return false; - } - - if (!cJSON_Compare(a_element, b_element, case_sensitive)) - { - return false; - } - } - - /* doing this twice, once on a and b to prevent true comparison if a subset of b - * TODO: Do this the proper way, this is just a fix for now */ - cJSON_ArrayForEach(b_element, b) - { - a_element = get_object_item(a, b_element->string, case_sensitive); - if (a_element == NULL) - { - return false; - } - - if (!cJSON_Compare(b_element, a_element, case_sensitive)) - { - return false; - } - } - - return true; - } - - default: - return false; - } -} - -CJSON_PUBLIC(void *) cJSON_malloc(size_t size) -{ - return global_hooks.allocate(size); -} - -CJSON_PUBLIC(void) cJSON_free(void *object) -{ - global_hooks.deallocate(object); -} diff --git a/SSH/RPM/rublonPam-1.0/lib/cJSON.h b/SSH/RPM/rublonPam-1.0/lib/cJSON.h deleted file mode 100644 index 0b05217..0000000 --- a/SSH/RPM/rublonPam-1.0/lib/cJSON.h +++ /dev/null @@ -1,277 +0,0 @@ -/* - Copyright (c) 2009-2017 Dave Gamble and cJSON contributors - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -#ifndef cJSON__h -#define cJSON__h - -#ifdef __cplusplus -extern "C" -{ -#endif - -#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) -#define __WINDOWS__ -#endif - -#ifdef __WINDOWS__ - -/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: -CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols -CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) -CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol -For *nix builds that support visibility attribute, you can define similar behavior by -setting default visibility to hidden by adding --fvisibility=hidden (for gcc) -or --xldscope=hidden (for sun cc) -to CFLAGS -then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does -*/ - -#define CJSON_CDECL __cdecl -#define CJSON_STDCALL __stdcall - -/* export symbols by default, this is necessary for copy pasting the C and header file */ -#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) -#define CJSON_EXPORT_SYMBOLS -#endif - -#if defined(CJSON_HIDE_SYMBOLS) -#define CJSON_PUBLIC(type) type CJSON_STDCALL -#elif defined(CJSON_EXPORT_SYMBOLS) -#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL -#elif defined(CJSON_IMPORT_SYMBOLS) -#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL -#endif -#else /* !__WINDOWS__ */ -#define CJSON_CDECL -#define CJSON_STDCALL - -#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) -#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type -#else -#define CJSON_PUBLIC(type) type -#endif -#endif - -/* project version */ -#define CJSON_VERSION_MAJOR 1 -#define CJSON_VERSION_MINOR 7 -#define CJSON_VERSION_PATCH 10 - -#include - -/* cJSON Types: */ -#define cJSON_Invalid (0) -#define cJSON_False (1 << 0) -#define cJSON_True (1 << 1) -#define cJSON_NULL (1 << 2) -#define cJSON_Number (1 << 3) -#define cJSON_String (1 << 4) -#define cJSON_Array (1 << 5) -#define cJSON_Object (1 << 6) -#define cJSON_Raw (1 << 7) /* raw json */ - -#define cJSON_IsReference 256 -#define cJSON_StringIsConst 512 - -/* The cJSON structure: */ -typedef struct cJSON -{ - /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ - struct cJSON *next; - struct cJSON *prev; - /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ - struct cJSON *child; - - /* The type of the item, as above. */ - int type; - - /* The item's string, if type==cJSON_String and type == cJSON_Raw */ - char *valuestring; - /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ - int valueint; - /* The item's number, if type==cJSON_Number */ - double valuedouble; - - /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ - char *string; -} cJSON; - -typedef struct cJSON_Hooks -{ - /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ - void *(CJSON_CDECL *malloc_fn)(size_t sz); - void (CJSON_CDECL *free_fn)(void *ptr); -} cJSON_Hooks; - -typedef int cJSON_bool; - -/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. - * This is to prevent stack overflows. */ -#ifndef CJSON_NESTING_LIMIT -#define CJSON_NESTING_LIMIT 1000 -#endif - -/* returns the version of cJSON as a string */ -CJSON_PUBLIC(const char*) cJSON_Version(void); - -/* Supply malloc, realloc and free functions to cJSON */ -CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); - -/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ -/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ -CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); -/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ -/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ -CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); - -/* Render a cJSON entity to text for transfer/storage. */ -CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); -/* Render a cJSON entity to text for transfer/storage without any formatting. */ -CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); -/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ -CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); -/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ -/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ -CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); -/* Delete a cJSON entity and all subentities. */ -CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); - -/* Returns the number of items in an array (or object). */ -CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); -/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ -CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); -/* Get item "string" from object. Case insensitive. */ -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); -CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); -/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ -CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); - -/* Check if the item is a string and return its valuestring */ -CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); - -/* These functions check the type of an item */ -CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); - -/* These calls create a cJSON item of the appropriate type. */ -CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); -CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); -CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); -/* raw json */ -CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); -CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); - -/* Create a string where valuestring references a string so - * it will not be freed by cJSON_Delete */ -CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); -/* Create an object/arrray that only references it's elements so - * they will not be freed by cJSON_Delete */ -CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); -CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); - -/* These utilities create an Array of count items. */ -CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); -CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); -CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); -CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); - -/* Append item to the specified array/object. */ -CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); -CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); -/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. - * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before - * writing to `item->string` */ -CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); -/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ -CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); - -/* Remove/Detatch items from Arrays/Objects. */ -CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); -CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); -CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); -CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); - -/* Update array items. */ -CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); -CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); -CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); -CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); - -/* Duplicate a cJSON item */ -CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); -/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will -need to be released. With recurse!=0, it will duplicate any children connected to the item. -The item->next and ->prev pointers are always zero on return from Duplicate. */ -/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. - * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ -CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); - - -CJSON_PUBLIC(void) cJSON_Minify(char *json); - -/* Helper functions for creating and adding items to an object at the same time. - * They return the added item or NULL on failure. */ -CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); -CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); -CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); -CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); -CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); -CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); -CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); -CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); -CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); - -/* When assigning an integer value, it needs to be propagated to valuedouble too. */ -#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) -/* helper for the cJSON_SetNumberValue macro */ -CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); -#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) - -/* Macro for iterating over an array or object */ -#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) - -/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ -CJSON_PUBLIC(void *) cJSON_malloc(size_t size); -CJSON_PUBLIC(void) cJSON_free(void *object); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/SSH/RPM/rublonPam-1.0/lib/cfg_parse.c b/SSH/RPM/rublonPam-1.0/lib/cfg_parse.c deleted file mode 100644 index c1738bf..0000000 --- a/SSH/RPM/rublonPam-1.0/lib/cfg_parse.c +++ /dev/null @@ -1,568 +0,0 @@ -#include "cfg_parse.h" - -/* for malloc, EXIT_SUCCESS and _FAILURE, exit */ -#include -/* for FILE*, fgets, fputs */ -#include -/* for memset, strlen, strchr etc */ -#include -/* for tolower, isspace */ -#include - -/* SIZE_MAX */ -#if __STDC_VERSION__ >= 199901L - #include -#elif !defined(SIZE_MAX) - #define SIZE_MAX ((size_t)-1) -#endif - -/* ************************************************************************* */ -/* implementation details of (opaque) config structures */ -/* ************************************************************************* */ - -struct cfg_node -{ - char* key; - char* value; - - struct cfg_node* next; -}; - -struct cfg_struct -{ - struct cfg_node* head; -}; - -/* ************************************************************************* */ -/* Helper functions */ -/* ************************************************************************* */ - -/* A malloc() wrapper which handles null return values */ -static void* cfg_malloc(const size_t size) -{ - void* ptr = - malloc(size); - - if (ptr == NULL) - { - perror("cfg_parse: ERROR: malloc() returned NULL"); - exit(EXIT_FAILURE); - } - - return ptr; -} - -/* Returns a duplicate of input str, without leading / trailing whitespace - Input str *MUST* be null-terminated, or disaster will result */ -static char* cfg_trim(const char* str) -{ - size_t tlen; - char* tstr; - - /* advance start pointer to first non-whitespace char */ - while (isspace(*str)) - str ++; - - /* roll back length until we run out of whitespace */ - tlen = strlen(str); - while (tlen > 0 && isspace(str[tlen - 1])) - tlen --; - - /* copy portion of string to new string */ - tstr = cfg_malloc(tlen + 1); - tstr[tlen] = '\0'; - if (tlen > 0) memcpy(tstr, str, tlen); - - return tstr; -} - -/* Returns a duplicate of input str, without leading / trailing whitespace - Also lowercases the string, AND returns NULL instead of empty str */ -static char* cfg_norm_key(const char* key) -{ - size_t i, tlen; - char* tkey; - - /* advance start pointer to first non-whitespace char */ - while (isspace(*key)) - key ++; - - /* roll back length until we run out of whitespace */ - tlen = strlen(key); - while (tlen > 0 && isspace(key[tlen - 1])) - tlen --; - - /* Exclude empty key */ - if (tlen == 0) return NULL; - - /* copy portion of string to new string */ - tkey = cfg_malloc(tlen + 1); - tkey[tlen] = '\0'; - /* Lowercase key and copy */ - for (i = 0; i < tlen; i++) - tkey[i] = tolower(key[i]); - - return tkey; -} - -/* Creates a node struct with key and value, and returns it */ -static struct cfg_node* cfg_create_node(char* key, char* value) -{ - struct cfg_node* cur = - cfg_malloc(sizeof(struct cfg_node)); - - /* assign key, value */ - cur->key = key; - cur->value = value; - cur->next = NULL; - - return cur; -} - -/* ************************************************************************* */ -/* Public functions */ -/* ************************************************************************* */ - -/** - * This function initializes a cfg_struct, and must be called before - * performing any further operations. - * @return Pointer to newly initialized cfg_struct object. - */ -struct cfg_struct* cfg_init() -{ - struct cfg_struct* cfg = - cfg_malloc(sizeof(struct cfg_struct)); - cfg->head = NULL; - - return cfg; -} - -/** - * This function deletes an entire cfg_struct, clearing any memory - * previously held by the structure. - * @param cfg Pointer to cfg_struct to delete. - */ -void cfg_free(struct cfg_struct* cfg) -{ - struct cfg_node* temp; - - if (cfg == NULL) return; - - while ((temp = cfg->head) != NULL) - { - cfg->head = temp->next; - - free(temp->key); - free(temp->value); - free(temp); - } - - free(cfg); -} - -/** - * This function loads data from a file, and inserts / updates the specified - * cfg_struct. New keys will be inserted. Existing keys will have values - * overwritten by those read from the file. - * The format of config-files is "key=value", with any amount of whitespace. - * Comments can be added, beginning with a # character until end-of-line. - * The maximum line size is CFG_MAX_LINE bytes (see cfg_parse.h) - * @param cfg Pointer to cfg_struct to update. - * @param filename String containing filename to open and parse. - * @return EXIT_SUCCESS (0) on success, or EXIT_FAILURE if file could not be - * opened. - */ -int cfg_load(struct cfg_struct* cfg, const char* filename) -{ - FILE* fp; - char* delim; - char buffer[CFG_MAX_LINE + 1]; - - /* safety check: null input */ - if (cfg == NULL || filename == NULL) return EXIT_FAILURE; - - /* open file for reading */ - fp = fopen(filename, "r"); - if (fp == NULL) return EXIT_FAILURE; - - while (!feof(fp)) - { - if (fgets(buffer, CFG_MAX_LINE + 1, fp) != NULL) - { - /* locate first # sign and terminate string there (comment) */ - delim = strchr(buffer, '#'); - if (delim != NULL) *delim = '\0'; - - /* locate first = sign and prepare to split */ - delim = strchr(buffer, '='); - if (delim != NULL) - { - *delim = '\0'; - delim ++; - - cfg_set(cfg, buffer, delim); - } - /* else: seems to be an invalid line */ - } - /* else: read error */ - } - - fclose(fp); - return EXIT_SUCCESS; -} - -/** - * This function saves a complete cfg_struct to a file. - * Comments are not preserved. - * @param cfg Pointer to cfg_struct to save. - * @param filename String containing filename to open and parse. - * @return EXIT_SUCCESS (0) on success, or EXIT_FAILURE if file could not be - * opened or a write error occurred. - */ -int cfg_save(const struct cfg_struct* cfg, const char* filename) -{ - FILE* fp; - struct cfg_node* cur; - - /* safety check: null input */ - if (cfg == NULL || filename == NULL) return EXIT_FAILURE; - - /* open output file for writing */ - fp = fopen(filename, "w"); - if (fp == NULL) return EXIT_FAILURE; - - /* point at first item in list */ - cur = cfg->head; - /* step through the list, dumping each key-value pair to disk */ - while (cur != NULL) - { - if (fputs(cur->key, fp) == EOF || - fputc('=', fp) == EOF || - fputs(cur->value, fp) == EOF || - fputc('\n', fp) == EOF) - { - fclose(fp); - return EXIT_FAILURE; - } - - cur = cur->next; - } - - fclose(fp); - return EXIT_SUCCESS; -} - -/** - * This function performs a key-lookup on a cfg_struct, and returns the - * associated value. - * @param cfg Pointer to cfg_struct to search. - * @param key String containing key to search for. - * @return String containing associated value, or NULL if key was not found. - */ -const char* cfg_get(const struct cfg_struct* cfg, const char* key) -{ - char* tkey; - struct cfg_node* cur; - - /* safety check: null input */ - if (cfg == NULL || cfg->head == NULL || key == NULL) return NULL; - - /* Trim input search key */ - tkey = cfg_norm_key(key); - /* Exclude empty key */ - if (tkey == NULL) return NULL; - - /* set up pointer to start of list */ - cur = cfg->head; - /* loop through linked list looking for match on key - if found, free search key, return the value */ - do - { - if (strcmp(tkey, cur->key) == 0) - { - free(tkey); - return cur->value; - } - } while ((cur = cur->next) != NULL); - - free(tkey); - return NULL; -} - -/** - * Returns an array of strings, one for each key in the config struct. - * This array and its contents are dynamically allocated: the caller - * should free them when done. - * @param cfg Pointer to cfg_struct to retrieve keys from. - * @param count Output parameter: number of keys in the returned array. - * @return Array of (count) strings, one for each key in the struct, or NULL - * in case of error or empty struct. - */ -char** cfg_get_keys(const struct cfg_struct* cfg, size_t* count) -{ - size_t i; - char** keys; - struct cfg_node* cur; - - /* safety check: null input */ - if (cfg == NULL || cfg->head == NULL) return NULL; - - /* walk the list to count how many keys we have available */ - i = 0; - for (cur = cfg->head; cur != NULL; cur = cur->next) - i ++; - - /* now create the array to hold them all */ - if (SIZE_MAX / sizeof(char*) < i) return NULL; - keys = cfg_malloc(i * sizeof(char*)); - - /* walk the list again, this time allocating and copying each key */ - cur = cfg->head; - for (*count = 0; *count < i; (*count) ++) - { - /* create space to hold the key, and copy it over */ - keys[*count] = cfg_malloc(strlen(cur->key) + 1); - strcpy(keys[*count], cur->key); - cur = cur->next; - } - - return keys; -} - -/** - * This function sets a single key-value pair in a cfg_struct. - * If the key already exists, its value will be updated. - * If not, a new item is added to the cfg_struct list. - * For convenience, a NULL value is treated as a call to cfg_delete(). - * @param cfg Pointer to cfg_struct to search. - * @param key String containing key to search for. - * @param value String containing new value to assign to key. - */ -void cfg_set(struct cfg_struct* cfg, const char* key, const char* value) -{ - char* tkey; - char* tvalue; - - struct cfg_node* cur; - - /* Treat NULL value as a "delete" operation */ - if (value == NULL) - { - cfg_delete(cfg, key); - return; - } - - /* safety check: null input */ - if (cfg == NULL || key == NULL) return; - - /* Trim input search key */ - tkey = cfg_norm_key(key); - /* Exclude empty key */ - if (tkey == NULL) return; - - /* Trim value. */ - tvalue = cfg_trim(value); - - if (cfg->head == NULL) - { - /* list was empty to begin with */ - cfg->head = cfg_create_node(tkey, tvalue); - } else { - struct cfg_node* prev; - - /* search list for existing key */ - cur = cfg->head; - do - { - if (strcmp(tkey, cur->key) == 0) - { - /* found a match: no longer need cur key */ - free(tkey); - - /* update value */ - free(cur->value); - cur->value = tvalue; - return; - } - prev = cur; - } while ((cur = cur->next) != NULL); - - /* not found: create new element and append it */ - prev->next = cfg_create_node(tkey, tvalue); - } -} - -/** - * This function sets multiple key-value pairs in a cfg_struct. - * @param cfg Pointer to cfg_struct to search. - * @param keys Array of strings containing key to search for. - * @param values Array of strings containing new value to assign to key. - * @param count Length of keys / values arrays - */ -void cfg_set_array(struct cfg_struct* cfg, const char* keys[], const char* values[], const size_t count) -{ - size_t i; - - /* safety check: null input */ - if (cfg == NULL || keys == NULL || values == NULL || count == 0) return; - - /* Call cfg_set on every item in the lists */ - for (i = 0; i < count; i ++) - cfg_set(cfg, keys[i], values[i]); -} - -/** - * This function deletes a key-value pair from a cfg_struct. - * If the key does not exist, the function does nothing. - * @param cfg Pointer to cfg_struct to search. - * @param key String containing key to search for. - */ -void cfg_delete(struct cfg_struct* cfg, const char* key) -{ - char* tkey; - struct cfg_node* cur; - struct cfg_node* prev; - - /* safety check: null input */ - if (cfg == NULL || cfg->head == NULL || key == NULL) return; - - /* Trim input search key */ - tkey = cfg_norm_key(key); - /* Exclude empty key */ - if (tkey == NULL) return; - - /* search list for existing key */ - cur = cfg->head; - do - { - if (strcmp(tkey, cur->key) == 0) - { - /* found it - cleanup trimmed key */ - free(tkey); - - if (cur == cfg->head) - { - /* first element */ - cfg->head = cur->next; - } else { - /* splice out element */ - prev->next = cur->next; - } - - /* delete element */ - free(cur->value); - free(cur->key); - free(cur); - - return; - } - - prev = cur; - } while ((cur = cur->next) != NULL); - /* not found */ - - /* cleanup trimmed key */ - free(tkey); -} - -/** - * This function deletes multiple key-value pairs from a cfg_struct. - * @param cfg Pointer to cfg_struct to search. - * @param keys Array of strings containing key to search for. - * @param count Length of keys array - */ -void cfg_delete_array(struct cfg_struct* cfg, const char* keys[], const size_t count) -{ - size_t i; - - /* safety check: null input */ - if (cfg == NULL || cfg->head == NULL || keys == NULL || count == 0) return; - - /* Call cfg_delete on every item in the list */ - for (i = 0; i < count; i ++) - cfg_delete(cfg, keys[i]); -} - -/** - * This function performs the inverse of cfg_delete_array(). - * Instead of deleting entries from cfg which match keys[], - * this will KEEP only those entries that match keys[]. - * It can be used to keep a config file tidy between versions or - * after user edits. - * @param cfg Pointer to cfg_struct to search. - * @param keys Array of strings containing keys to keep - * @param count Length of keys array - */ -void cfg_prune(struct cfg_struct* cfg, const char* keys[], const size_t count) -{ - char** tkeys; - size_t i, j; - - struct cfg_node* cur; - struct cfg_node* prev; - - /* safety check: null input */ - if (cfg == NULL || cfg->head == NULL || keys == NULL || count == 0 || - SIZE_MAX / sizeof(char*) < count) return; - - /* First we must prep every key in keys[] using the normalize function. */ - tkeys = cfg_malloc(count * sizeof(char*)); - - j = 0; - for (i = 0; i < count; i ++) - { - char *tkey; - - if (keys[i] == NULL) continue; - tkey = cfg_norm_key(keys[i]); - if (tkey == NULL) continue; - - tkeys[j] = tkey; - j ++; - } - - if (j == 0) - { - free(tkeys); - return; - } - - /* Now iterate through the cfg struct and test every entry */ - cur = cfg->head; - do - { - for (i = 0; i < j; i ++) - if (strcmp(tkeys[i], cur->key) == 0) - break; - - if (i == j) - { - /* Didn't find a key match - delete this */ - free(cur->value); - free(cur->key); - - if (cur == cfg->head) - { - /* first element */ - cfg->head = cur->next; - free(cur); - cur = cfg->head; - } else { - /* splice out element */ - prev->next = cur->next; - free(cur); - cur = prev->next; - } - } else { - /* matched, advance list element */ - prev = cur; - cur = cur->next; - } - } while (cur != NULL); - - /* Cleanup all our trimmed keys */ - for (i = 0; i < j; i ++) - free(tkeys[i]); - free(tkeys); -} - diff --git a/SSH/RPM/rublonPam-1.0/lib/cfg_parse.h b/SSH/RPM/rublonPam-1.0/lib/cfg_parse.h deleted file mode 100644 index 2d175aa..0000000 --- a/SSH/RPM/rublonPam-1.0/lib/cfg_parse.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * @file - * @brief Header file for cfg_parse. - * - * This file should be included to use any features of cfg_parse. Typically, a - * user should create a pointer to a cfg_struct, initialize it with cfg_init(), - * and then perform actions on that object (lookup, add, delete) by passing the - * pointer to the functions here. - * At end of use, call cfg_delete to clean up the object. - */ -#ifndef CFG_PARSE_H_ -#define CFG_PARSE_H_ - -#include - -/** - * @brief Sets the maximum size of a line in a configuration file. - * - * cfg_load uses this definition to limit the size of its read buffer. - * Lines which exceed the length do not crash outright, but probably won't - * load correctly. - */ -#define CFG_MAX_LINE 256 - -/* Opaque data structure holding config in memory */ -struct cfg_struct; - -/* Declare C-style name mangling, - this makes mixing with c++ compilers possible */ -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Creates a cfg_struct. - */ -struct cfg_struct* cfg_init(); - -/** - * @brief Frees a cfg_struct. - */ -void cfg_free(struct cfg_struct* cfg); - -/** - * @brief Loads key=value pairs from a file into cfg_struct. - */ -int cfg_load(struct cfg_struct* cfg, const char* filename); - -/** - * @brief Saves a cfg_struct to a file as key=value pairs. - */ -int cfg_save(const struct cfg_struct* cfg, const char* filename); - -/** - * @brief Retrieves a value from a cfg_struct for a specified key. - */ -const char* cfg_get(const struct cfg_struct* cfg, const char* key); - -/** - * @brief Retrieves a list of all keys in a cfg_struct. - */ -char** cfg_get_keys(const struct cfg_struct* cfg, size_t* count); - -/** - * @brief Sets a key, value pair in a cfg_struct. - */ -void cfg_set(struct cfg_struct* cfg, const char* key, const char* value); - -/** - * @brief Sets multiple key, value pairs in a cfg_struct. - */ -void cfg_set_array(struct cfg_struct* cfg, const char* keys[], const char* values[], size_t count); - -/** - * @brief Deletes a key (and associated value) from a cfg_struct. - */ -void cfg_delete(struct cfg_struct* cfg, const char* key); - -/** - * @brief Deletes multiple keys (and associated values) from a cfg_struct. - */ -void cfg_delete_array(struct cfg_struct* cfg, const char* keys[], size_t count); - -/** - * @brief Deletes all entries not found in keys[] from a cfg_struct. - */ -void cfg_prune(struct cfg_struct* cfg, const char* keys[], size_t count); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/SSH/RPM/rublonPam-1.0/lib/qrcodegen.c b/SSH/RPM/rublonPam-1.0/lib/qrcodegen.c deleted file mode 100644 index d239c31..0000000 --- a/SSH/RPM/rublonPam-1.0/lib/qrcodegen.c +++ /dev/null @@ -1,1025 +0,0 @@ -/* - * QR Code generator library (C) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#include -#include -#include -#include -#include "qrcodegen.h" - -#ifndef QRCODEGEN_TEST - #define testable static // Keep functions private -#else - #define testable // Expose private functions -#endif - - -/*---- Forward declarations for private functions ----*/ - -// Regarding all public and private functions defined in this source file: -// - They require all pointer/array arguments to be not null unless the array length is zero. -// - They only read input scalar/array arguments, write to output pointer/array -// arguments, and return scalar values; they are "pure" functions. -// - They don't read mutable global variables or write to any global variables. -// - They don't perform I/O, read the clock, print to console, etc. -// - They allocate a small and constant amount of stack memory. -// - They don't allocate or free any memory on the heap. -// - They don't recurse or mutually recurse. All the code -// could be inlined into the top-level public functions. -// - They run in at most quadratic time with respect to input arguments. -// Most functions run in linear time, and some in constant time. -// There are no unbounded loops or non-obvious termination conditions. -// - They are completely thread-safe if the caller does not give the -// same writable buffer to concurrent calls to these functions. - -testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen); - -testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); -testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); -testable int getNumRawDataModules(int ver); - -testable void calcReedSolomonGenerator(int degree, uint8_t result[]); -testable void calcReedSolomonRemainder(const uint8_t data[], int dataLen, - const uint8_t generator[], int degree, uint8_t result[]); -testable uint8_t finiteFieldMultiply(uint8_t x, uint8_t y); - -testable void initializeFunctionModules(int version, uint8_t qrcode[]); -static void drawWhiteFunctionModules(uint8_t qrcode[], int version); -static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]); -testable int getAlignmentPatternPositions(int version, uint8_t result[7]); -static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]); - -static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]); -static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask); -static long getPenaltyScore(const uint8_t qrcode[]); -static void addRunToHistory(unsigned char run, unsigned char history[7]); -static bool hasFinderLikePattern(const unsigned char runHistory[7]); - -testable bool getModule(const uint8_t qrcode[], int x, int y); -testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack); -testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack); -static bool getBit(int x, int i); - -testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars); -testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version); -static int numCharCountBits(enum qrcodegen_Mode mode, int version); - - - -/*---- Private tables of constants ----*/ - -// The set of all legal characters in alphanumeric mode, where each character -// value maps to the index in the string. For checking text and encoding segments. -static const char *ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; - -// For generating error correction codes. -testable const int8_t ECC_CODEWORDS_PER_BLOCK[4][41] = { - // Version: (note that index 0 is for padding, and is set to an illegal value) - //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level - {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low - {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium - {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile - {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High -}; - -#define qrcodegen_REED_SOLOMON_DEGREE_MAX 30 // Based on the table above - -// For generating error correction codes. -testable const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41] = { - // Version: (note that index 0 is for padding, and is set to an illegal value) - //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level - {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low - {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium - {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile - {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High -}; - -// For automatic mask pattern selection. -static const int PENALTY_N1 = 3; -static const int PENALTY_N2 = 3; -static const int PENALTY_N3 = 40; -static const int PENALTY_N4 = 10; - - - -/*---- High-level QR Code encoding functions ----*/ - -// Public function - see documentation comment in header file. -bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], - enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { - size_t i; - size_t textLen = strlen(text); - if (textLen == 0) - return qrcodegen_encodeSegmentsAdvanced(NULL, 0, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); - size_t bufLen = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); - - struct qrcodegen_Segment seg; - if (qrcodegen_isNumeric(text)) { - if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, textLen) > bufLen) - goto fail; - seg = qrcodegen_makeNumeric(text, tempBuffer); - } else if (qrcodegen_isAlphanumeric(text)) { - if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ALPHANUMERIC, textLen) > bufLen) - goto fail; - seg = qrcodegen_makeAlphanumeric(text, tempBuffer); - } else { - if (textLen > bufLen) - goto fail; - for (i = 0; i < textLen; i++) - tempBuffer[i] = (uint8_t)text[i]; - seg.mode = qrcodegen_Mode_BYTE; - seg.bitLength = calcSegmentBitLength(seg.mode, textLen); - if (seg.bitLength == -1) - goto fail; - seg.numChars = (int)textLen; - seg.data = tempBuffer; - } - return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); - -fail: - qrcode[0] = 0; // Set size to invalid value for safety - return false; -} - - -// Public function - see documentation comment in header file. -bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], - enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { - - struct qrcodegen_Segment seg; - seg.mode = qrcodegen_Mode_BYTE; - seg.bitLength = calcSegmentBitLength(seg.mode, dataLen); - if (seg.bitLength == -1) { - qrcode[0] = 0; // Set size to invalid value for safety - return false; - } - seg.numChars = (int)dataLen; - seg.data = dataAndTemp; - return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, dataAndTemp, qrcode); -} - - -// Appends the given number of low-order bits of the given value to the given byte-based -// bit buffer, increasing the bit length. Requires 0 <= numBits <= 16 and val < 2^numBits. -testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen) { - int i; - assert(0 <= numBits && numBits <= 16 && (unsigned long)val >> numBits == 0); - for (i = numBits - 1; i >= 0; i--, (*bitLen)++) - buffer[*bitLen >> 3] |= ((val >> i) & 1) << (7 - (*bitLen & 7)); -} - - - -/*---- Low-level QR Code encoding functions ----*/ - -// Public function - see documentation comment in header file. -bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, - enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]) { - return qrcodegen_encodeSegmentsAdvanced(segs, len, ecl, - qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true, tempBuffer, qrcode); -} - - -// Public function - see documentation comment in header file. -bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, - int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]) { - - int i,j; - uint8_t padByte; - size_t k; - assert(segs != NULL || len == 0); - assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); - assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7); - - // Find the minimal version number to use - int version, dataUsedBits; - for (version = minVersion; ; version++) { - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available - dataUsedBits = getTotalBits(segs, len, version); - if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) - break; // This version number is found to be suitable - if (version >= maxVersion) { // All versions in the range could not fit the given data - qrcode[0] = 0; // Set size to invalid value for safety - return false; - } - } - assert(dataUsedBits != -1); - - // Increase the error correction level while the data still fits in the current version number - for (i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) { // From low to high - if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8) - ecl = (enum qrcodegen_Ecc)i; - } - - // Concatenate all segments to create the data bit string - memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); - int bitLen = 0; - for (k = 0; k < len; k++) { - const struct qrcodegen_Segment *seg = &segs[k]; - appendBitsToBuffer((int)seg->mode, 4, qrcode, &bitLen); - appendBitsToBuffer(seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); - for (j = 0; j < seg->bitLength; j++) - appendBitsToBuffer((seg->data[j >> 3] >> (7 - (j & 7))) & 1, 1, qrcode, &bitLen); - } - assert(bitLen == dataUsedBits); - - // Add terminator and pad up to a byte if applicable - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; - assert(bitLen <= dataCapacityBits); - int terminatorBits = dataCapacityBits - bitLen; - if (terminatorBits > 4) - terminatorBits = 4; - appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen); - appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen); - assert(bitLen % 8 == 0); - - // Pad with alternating bytes until data capacity is reached - for (padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - appendBitsToBuffer(padByte, 8, qrcode, &bitLen); - - // Draw function and data codeword modules - addEccAndInterleave(qrcode, version, ecl, tempBuffer); - initializeFunctionModules(version, qrcode); - drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); - drawWhiteFunctionModules(qrcode, version); - initializeFunctionModules(version, tempBuffer); - - // Handle masking - if (mask == qrcodegen_Mask_AUTO) { // Automatically choose best mask - long minPenalty = LONG_MAX; - for (i = 0; i < 8; i++) { - enum qrcodegen_Mask msk = (enum qrcodegen_Mask)i; - applyMask(tempBuffer, qrcode, msk); - drawFormatBits(ecl, msk, qrcode); - long penalty = getPenaltyScore(qrcode); - if (penalty < minPenalty) { - mask = msk; - minPenalty = penalty; - } - applyMask(tempBuffer, qrcode, msk); // Undoes the mask due to XOR - } - } - assert(0 <= (int)mask && (int)mask <= 7); - applyMask(tempBuffer, qrcode, mask); - drawFormatBits(ecl, mask, qrcode); - return true; -} - - - -/*---- Error correction code generation functions ----*/ - -// Appends error correction bytes to each block of the given data array, then interleaves -// bytes from the blocks and stores them in the result array. data[0 : dataLen] contains -// the input data. data[dataLen : rawCodewords] is used as a temporary work area and will -// be clobbered by this function. The final answer is stored in result[0 : rawCodewords]. -testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]) { - int i,j,k; - // Calculate parameter numbers - assert(0 <= (int)ecl && (int)ecl < 4 && qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); - int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; - int blockEccLen = ECC_CODEWORDS_PER_BLOCK [(int)ecl][version]; - int rawCodewords = getNumRawDataModules(version) / 8; - int dataLen = getNumDataCodewords(version, ecl); - int numShortBlocks = numBlocks - rawCodewords % numBlocks; - int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; - - // Split data into blocks, calculate ECC, and interleave - // (not concatenate) the bytes into a single sequence - uint8_t generator[qrcodegen_REED_SOLOMON_DEGREE_MAX]; - calcReedSolomonGenerator(blockEccLen, generator); - const uint8_t *dat = data; - for (i = 0; i < numBlocks; i++) { - int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); - uint8_t *ecc = &data[dataLen]; // Temporary storage - calcReedSolomonRemainder(dat, datLen, generator, blockEccLen, ecc); - for (j = 0, k = i; j < datLen; j++, k += numBlocks) { // Copy data - if (j == shortBlockDataLen) - k -= numShortBlocks; - result[k] = dat[j]; - } - for (j = 0, k = dataLen + i; j < blockEccLen; j++, k += numBlocks) // Copy ECC - result[k] = ecc[j]; - dat += datLen; - } -} - - -// Returns the number of 8-bit codewords that can be used for storing data (not ECC), -// for the given version number and error correction level. The result is in the range [9, 2956]. -testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl) { - int v = version, e = (int)ecl; - assert(0 <= e && e < 4); - return getNumRawDataModules(v) / 8 - - ECC_CODEWORDS_PER_BLOCK [e][v] - * NUM_ERROR_CORRECTION_BLOCKS[e][v]; -} - - -// Returns the number of data bits that can be stored in a QR Code of the given version number, after -// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. -// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. -testable int getNumRawDataModules(int ver) { - assert(qrcodegen_VERSION_MIN <= ver && ver <= qrcodegen_VERSION_MAX); - int result = (16 * ver + 128) * ver + 64; - if (ver >= 2) { - int numAlign = ver / 7 + 2; - result -= (25 * numAlign - 10) * numAlign - 55; - if (ver >= 7) - result -= 36; - } - return result; -} - - - -/*---- Reed-Solomon ECC generator functions ----*/ - -// Calculates the Reed-Solomon generator polynomial of the given degree, storing in result[0 : degree]. -testable void calcReedSolomonGenerator(int degree, uint8_t result[]) { - int i,j; - // Start with the monomial x^0 - assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); - memset(result, 0, degree * sizeof(result[0])); - result[degree - 1] = 1; - - // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), - // drop the highest term, and store the rest of the coefficients in order of descending powers. - // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). - uint8_t root = 1; - for (i = 0; i < degree; i++) { - // Multiply the current product by (x - r^i) - for (j = 0; j < degree; j++) { - result[j] = finiteFieldMultiply(result[j], root); - if (j + 1 < degree) - result[j] ^= result[j + 1]; - } - root = finiteFieldMultiply(root, 0x02); - } -} - - -// Calculates the remainder of the polynomial data[0 : dataLen] when divided by the generator[0 : degree], where all -// polynomials are in big endian and the generator has an implicit leading 1 term, storing the result in result[0 : degree]. -testable void calcReedSolomonRemainder(const uint8_t data[], int dataLen, - const uint8_t generator[], int degree, uint8_t result[]) { - int i,j; - // Perform polynomial division - assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); - memset(result, 0, degree * sizeof(result[0])); - for (i = 0; i < dataLen; i++) { - uint8_t factor = data[i] ^ result[0]; - memmove(&result[0], &result[1], (degree - 1) * sizeof(result[0])); - result[degree - 1] = 0; - for (j = 0; j < degree; j++) - result[j] ^= finiteFieldMultiply(generator[j], factor); - } -} - -#undef qrcodegen_REED_SOLOMON_DEGREE_MAX - - -// Returns the product of the two given field elements modulo GF(2^8/0x11D). -// All inputs are valid. This could be implemented as a 256*256 lookup table. -testable uint8_t finiteFieldMultiply(uint8_t x, uint8_t y) { - int i; - // Russian peasant multiplication - uint8_t z = 0; - for (i = 7; i >= 0; i--) { - z = (z << 1) ^ ((z >> 7) * 0x11D); - z ^= ((y >> i) & 1) * x; - } - return z; -} - - - -/*---- Drawing function modules ----*/ - -// Clears the given QR Code grid with white modules for the given -// version's size, then marks every function module as black. -testable void initializeFunctionModules(int version, uint8_t qrcode[]) { - int i,j; - // Initialize QR Code - int qrsize = version * 4 + 17; - memset(qrcode, 0, ((qrsize * qrsize + 7) / 8 + 1) * sizeof(qrcode[0])); - qrcode[0] = (uint8_t)qrsize; - - // Fill horizontal and vertical timing patterns - fillRectangle(6, 0, 1, qrsize, qrcode); - fillRectangle(0, 6, qrsize, 1, qrcode); - - // Fill 3 finder patterns (all corners except bottom right) and format bits - fillRectangle(0, 0, 9, 9, qrcode); - fillRectangle(qrsize - 8, 0, 8, 9, qrcode); - fillRectangle(0, qrsize - 8, 9, 8, qrcode); - - // Fill numerous alignment patterns - uint8_t alignPatPos[7]; - int numAlign = getAlignmentPatternPositions(version, alignPatPos); - for (i = 0; i < numAlign; i++) { - for (j = 0; j < numAlign; j++) { - // Don't draw on the three finder corners - if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) - fillRectangle(alignPatPos[i] - 2, alignPatPos[j] - 2, 5, 5, qrcode); - } - } - - // Fill version blocks - if (version >= 7) { - fillRectangle(qrsize - 11, 0, 3, 6, qrcode); - fillRectangle(0, qrsize - 11, 6, 3, qrcode); - } -} - - -// Draws white function modules and possibly some black modules onto the given QR Code, without changing -// non-function modules. This does not draw the format bits. This requires all function modules to be previously -// marked black (namely by initializeFunctionModules()), because this may skip redrawing black function modules. -static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { - int i,j,dx,dy; - // Draw horizontal and vertical timing patterns - int qrsize = qrcodegen_getSize(qrcode); - for (i = 7; i < qrsize - 7; i += 2) { - setModule(qrcode, 6, i, false); - setModule(qrcode, i, 6, false); - } - - // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) - for (dy = -4; dy <= 4; dy++) { - for (dx = -4; dx <= 4; dx++) { - int dist = abs(dx); - if (abs(dy) > dist) - dist = abs(dy); - if (dist == 2 || dist == 4) { - setModuleBounded(qrcode, 3 + dx, 3 + dy, false); - setModuleBounded(qrcode, qrsize - 4 + dx, 3 + dy, false); - setModuleBounded(qrcode, 3 + dx, qrsize - 4 + dy, false); - } - } - } - - // Draw numerous alignment patterns - uint8_t alignPatPos[7]; - int numAlign = getAlignmentPatternPositions(version, alignPatPos); - for (i = 0; i < numAlign; i++) { - for (j = 0; j < numAlign; j++) { - if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) - continue; // Don't draw on the three finder corners - for (dy = -1; dy <= 1; dy++) { - for (dx = -1; dx <= 1; dx++) - setModule(qrcode, alignPatPos[i] + dx, alignPatPos[j] + dy, dx == 0 && dy == 0); - } - } - } - - // Draw version blocks - if (version >= 7) { - // Calculate error correction code and pack bits - int rem = version; // version is uint6, in the range [7, 40] - for (i = 0; i < 12; i++) - rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); - long bits = (long)version << 12 | rem; // uint18 - assert(bits >> 18 == 0); - - // Draw two copies - for (i = 0; i < 6; i++) { - for (j = 0; j < 3; j++) { - int k = qrsize - 11 + j; - setModule(qrcode, k, i, (bits & 1) != 0); - setModule(qrcode, i, k, (bits & 1) != 0); - bits >>= 1; - } - } - } -} - - -// Draws two copies of the format bits (with its own error correction code) based -// on the given mask and error correction level. This always draws all modules of -// the format bits, unlike drawWhiteFunctionModules() which might skip black modules. -static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]) { - int i; - // Calculate error correction code and pack bits - assert(0 <= (int)mask && (int)mask <= 7); - static const int table[] = {1, 0, 3, 2}; - int data = table[(int)ecl] << 3 | (int)mask; // errCorrLvl is uint2, mask is uint3 - int rem = data; - for (i = 0; i < 10; i++) - rem = (rem << 1) ^ ((rem >> 9) * 0x537); - int bits = (data << 10 | rem) ^ 0x5412; // uint15 - assert(bits >> 15 == 0); - - // Draw first copy - for (i = 0; i <= 5; i++) - setModule(qrcode, 8, i, getBit(bits, i)); - setModule(qrcode, 8, 7, getBit(bits, 6)); - setModule(qrcode, 8, 8, getBit(bits, 7)); - setModule(qrcode, 7, 8, getBit(bits, 8)); - for (i = 9; i < 15; i++) - setModule(qrcode, 14 - i, 8, getBit(bits, i)); - - // Draw second copy - int qrsize = qrcodegen_getSize(qrcode); - for (i = 0; i < 8; i++) - setModule(qrcode, qrsize - 1 - i, 8, getBit(bits, i)); - for (i = 8; i < 15; i++) - setModule(qrcode, 8, qrsize - 15 + i, getBit(bits, i)); - setModule(qrcode, 8, qrsize - 8, true); // Always black -} - - -// Calculates and stores an ascending list of positions of alignment patterns -// for this version number, returning the length of the list (in the range [0,7]). -// Each position is in the range [0,177), and are used on both the x and y axes. -// This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. -testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { - int i, pos; - if (version == 1) - return 0; - int numAlign = version / 7 + 2; - int step = (version == 32) ? 26 : - (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; - for (i = numAlign - 1, pos = version * 4 + 10; i >= 1; i--, pos -= step) - result[i] = pos; - result[0] = 6; - return numAlign; -} - - -// Sets every pixel in the range [left : left + width] * [top : top + height] to black. -static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]) { - int dx,dy; - for (dy = 0; dy < height; dy++) { - for (dx = 0; dx < width; dx++) - setModule(qrcode, left + dx, top + dy, true); - } -} - - - -/*---- Drawing data modules and masking ----*/ - -// Draws the raw codewords (including data and ECC) onto the given QR Code. This requires the initial state of -// the QR Code to be black at function modules and white at codeword modules (including unused remainder bits). -static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { - int right,vert,j; - int qrsize = qrcodegen_getSize(qrcode); - int i = 0; // Bit index into the data - // Do the funny zigzag scan - for (right = qrsize - 1; right >= 1; right -= 2) { // Index of right column in each column pair - if (right == 6) - right = 5; - for (vert = 0; vert < qrsize; vert++) { // Vertical counter - for (j = 0; j < 2; j++) { - int x = right - j; // Actual x coordinate - bool upward = ((right + 1) & 2) == 0; - int y = upward ? qrsize - 1 - vert : vert; // Actual y coordinate - if (!getModule(qrcode, x, y) && i < dataLen * 8) { - bool black = getBit(data[i >> 3], 7 - (i & 7)); - setModule(qrcode, x, y, black); - i++; - } - // If this QR Code has any remainder bits (0 to 7), they were assigned as - // 0/false/white by the constructor and are left unchanged by this method - } - } - } - assert(i == dataLen * 8); -} - - -// XORs the codeword modules in this QR Code with the given mask pattern. -// The function modules must be marked and the codeword bits must be drawn -// before masking. Due to the arithmetic of XOR, calling applyMask() with -// the same mask value a second time will undo the mask. A final well-formed -// QR Code needs exactly one (not zero, two, etc.) mask applied. -static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask) { - int x,y; - assert(0 <= (int)mask && (int)mask <= 7); // Disallows qrcodegen_Mask_AUTO - int qrsize = qrcodegen_getSize(qrcode); - for (y = 0; y < qrsize; y++) { - for (x = 0; x < qrsize; x++) { - if (getModule(functionModules, x, y)) - continue; - bool invert; - switch ((int)mask) { - case 0: invert = (x + y) % 2 == 0; break; - case 1: invert = y % 2 == 0; break; - case 2: invert = x % 3 == 0; break; - case 3: invert = (x + y) % 3 == 0; break; - case 4: invert = (x / 3 + y / 2) % 2 == 0; break; - case 5: invert = x * y % 2 + x * y % 3 == 0; break; - case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; - case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; - default: assert(false); return; - } - bool val = getModule(qrcode, x, y); - setModule(qrcode, x, y, val ^ invert); - } - } -} - - -// Calculates and returns the penalty score based on state of the given QR Code's current modules. -// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. -static long getPenaltyScore(const uint8_t qrcode[]) { - int qrsize = qrcodegen_getSize(qrcode); - long result = 0; - int x,y; - // Adjacent modules in row having same color, and finder-like patterns - for (y = 0; y < qrsize; y++) { - unsigned char runHistory[7] = {0}; - bool color = false; - unsigned char runX = 0; - for (x = 0; x < qrsize; x++) { - if (getModule(qrcode, x, y) == color) { - runX++; - if (runX == 5) - result += PENALTY_N1; - else if (runX > 5) - result++; - } else { - addRunToHistory(runX, runHistory); - if (!color && hasFinderLikePattern(runHistory)) - result += PENALTY_N3; - color = getModule(qrcode, x, y); - runX = 1; - } - } - addRunToHistory(runX, runHistory); - if (color) - addRunToHistory(0, runHistory); // Dummy run of white - if (hasFinderLikePattern(runHistory)) - result += PENALTY_N3; - } - // Adjacent modules in column having same color, and finder-like patterns - for (x = 0; x < qrsize; x++) { - unsigned char runHistory[7] = {0}; - bool color = false; - unsigned char runY = 0; - for (y = 0; y < qrsize; y++) { - if (getModule(qrcode, x, y) == color) { - runY++; - if (runY == 5) - result += PENALTY_N1; - else if (runY > 5) - result++; - } else { - addRunToHistory(runY, runHistory); - if (!color && hasFinderLikePattern(runHistory)) - result += PENALTY_N3; - color = getModule(qrcode, x, y); - runY = 1; - } - } - addRunToHistory(runY, runHistory); - if (color) - addRunToHistory(0, runHistory); // Dummy run of white - if (hasFinderLikePattern(runHistory)) - result += PENALTY_N3; - } - - // 2*2 blocks of modules having same color - for (y = 0; y < qrsize - 1; y++) { - for (x = 0; x < qrsize - 1; x++) { - bool color = getModule(qrcode, x, y); - if ( color == getModule(qrcode, x + 1, y) && - color == getModule(qrcode, x, y + 1) && - color == getModule(qrcode, x + 1, y + 1)) - result += PENALTY_N2; - } - } - - // Balance of black and white modules - int black = 0; - for (y = 0; y < qrsize; y++) { - for (x = 0; x < qrsize; x++) { - if (getModule(qrcode, x, y)) - black++; - } - } - int total = qrsize * qrsize; // Note that size is odd, so black/total != 1/2 - // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% - int k = (int)((labs(black * 20L - total * 10L) + total - 1) / total) - 1; - result += k * PENALTY_N4; - return result; -} - - -// Inserts the given value to the front of the given array, which shifts over the -// existing values and deletes the last value. A helper function for getPenaltyScore(). -static void addRunToHistory(unsigned char run, unsigned char history[7]) { - memmove(&history[1], &history[0], 6 * sizeof(history[0])); - history[0] = run; -} - - -// Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and -// surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). -// Must only be called immediately after a run of white modules has ended. -static bool hasFinderLikePattern(const unsigned char runHistory[7]) { - unsigned char n = runHistory[1]; - // The maximum QR Code size is 177, hence the run length n <= 177. - // Arithmetic is promoted to int, so n*4 will not overflow. - return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n - && runHistory[3] == n * 3 && (runHistory[0] >= n * 4 || runHistory[6] >= n * 4); -} - - - -/*---- Basic QR Code information ----*/ - -// Public function - see documentation comment in header file. -int qrcodegen_getSize(const uint8_t qrcode[]) { - assert(qrcode != NULL); - int result = qrcode[0]; - assert((qrcodegen_VERSION_MIN * 4 + 17) <= result - && result <= (qrcodegen_VERSION_MAX * 4 + 17)); - return result; -} - - -// Public function - see documentation comment in header file. -bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y) { - assert(qrcode != NULL); - int qrsize = qrcode[0]; - return (0 <= x && x < qrsize && 0 <= y && y < qrsize) && getModule(qrcode, x, y); -} - - -// Gets the module at the given coordinates, which must be in bounds. -testable bool getModule(const uint8_t qrcode[], int x, int y) { - int qrsize = qrcode[0]; - assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); - int index = y * qrsize + x; - return getBit(qrcode[(index >> 3) + 1], index & 7); -} - - -// Sets the module at the given coordinates, which must be in bounds. -testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack) { - int qrsize = qrcode[0]; - assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); - int index = y * qrsize + x; - int bitIndex = index & 7; - int byteIndex = (index >> 3) + 1; - if (isBlack) - qrcode[byteIndex] |= 1 << bitIndex; - else - qrcode[byteIndex] &= (1 << bitIndex) ^ 0xFF; -} - - -// Sets the module at the given coordinates, doing nothing if out of bounds. -testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack) { - int qrsize = qrcode[0]; - if (0 <= x && x < qrsize && 0 <= y && y < qrsize) - setModule(qrcode, x, y, isBlack); -} - - -// Returns true iff the i'th bit of x is set to 1. Requires x >= 0 and 0 <= i <= 14. -static bool getBit(int x, int i) { - return ((x >> i) & 1) != 0; -} - - - -/*---- Segment handling ----*/ - -// Public function - see documentation comment in header file. -bool qrcodegen_isAlphanumeric(const char *text) { - assert(text != NULL); - for (; *text != '\0'; text++) { - if (strchr(ALPHANUMERIC_CHARSET, *text) == NULL) - return false; - } - return true; -} - - -// Public function - see documentation comment in header file. -bool qrcodegen_isNumeric(const char *text) { - assert(text != NULL); - for (; *text != '\0'; text++) { - if (*text < '0' || *text > '9') - return false; - } - return true; -} - - -// Public function - see documentation comment in header file. -size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars) { - int temp = calcSegmentBitLength(mode, numChars); - if (temp == -1) - return SIZE_MAX; - assert(0 <= temp && temp <= INT16_MAX); - return ((size_t)temp + 7) / 8; -} - - -// Returns the number of data bits needed to represent a segment -// containing the given number of characters using the given mode. Notes: -// - Returns -1 on failure, i.e. numChars > INT16_MAX or -// the number of needed bits exceeds INT16_MAX (i.e. 32767). -// - Otherwise, all valid results are in the range [0, INT16_MAX]. -// - For byte mode, numChars measures the number of bytes, not Unicode code points. -// - For ECI mode, numChars must be 0, and the worst-case number of bits is returned. -// An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. -testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars) { - // All calculations are designed to avoid overflow on all platforms - if (numChars > (unsigned int)INT16_MAX) - return -1; - long result = (long)numChars; - if (mode == qrcodegen_Mode_NUMERIC) - result = (result * 10 + 2) / 3; // ceil(10/3 * n) - else if (mode == qrcodegen_Mode_ALPHANUMERIC) - result = (result * 11 + 1) / 2; // ceil(11/2 * n) - else if (mode == qrcodegen_Mode_BYTE) - result *= 8; - else if (mode == qrcodegen_Mode_KANJI) - result *= 13; - else if (mode == qrcodegen_Mode_ECI && numChars == 0) - result = 3 * 8; - else { // Invalid argument - assert(false); - return -1; - } - assert(result >= 0); - if (result > INT16_MAX) - return -1; - return (int)result; -} - - -// Public function - see documentation comment in header file. -struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]) { - assert(data != NULL || len == 0); - struct qrcodegen_Segment result; - result.mode = qrcodegen_Mode_BYTE; - result.bitLength = calcSegmentBitLength(result.mode, len); - assert(result.bitLength != -1); - result.numChars = (int)len; - if (len > 0) - memcpy(buf, data, len * sizeof(buf[0])); - result.data = buf; - return result; -} - - -// Public function - see documentation comment in header file. -struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]) { - assert(digits != NULL); - struct qrcodegen_Segment result; - size_t len = strlen(digits); - result.mode = qrcodegen_Mode_NUMERIC; - int bitLen = calcSegmentBitLength(result.mode, len); - assert(bitLen != -1); - result.numChars = (int)len; - if (bitLen > 0) - memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); - result.bitLength = 0; - - unsigned int accumData = 0; - int accumCount = 0; - for (; *digits != '\0'; digits++) { - char c = *digits; - assert('0' <= c && c <= '9'); - accumData = accumData * 10 + (unsigned int)(c - '0'); - accumCount++; - if (accumCount == 3) { - appendBitsToBuffer(accumData, 10, buf, &result.bitLength); - accumData = 0; - accumCount = 0; - } - } - if (accumCount > 0) // 1 or 2 digits remaining - appendBitsToBuffer(accumData, accumCount * 3 + 1, buf, &result.bitLength); - assert(result.bitLength == bitLen); - result.data = buf; - return result; -} - - -// Public function - see documentation comment in header file. -struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]) { - assert(text != NULL); - struct qrcodegen_Segment result; - size_t len = strlen(text); - result.mode = qrcodegen_Mode_ALPHANUMERIC; - int bitLen = calcSegmentBitLength(result.mode, len); - assert(bitLen != -1); - result.numChars = (int)len; - if (bitLen > 0) - memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); - result.bitLength = 0; - - unsigned int accumData = 0; - int accumCount = 0; - for (; *text != '\0'; text++) { - const char *temp = strchr(ALPHANUMERIC_CHARSET, *text); - assert(temp != NULL); - accumData = accumData * 45 + (unsigned int)(temp - ALPHANUMERIC_CHARSET); - accumCount++; - if (accumCount == 2) { - appendBitsToBuffer(accumData, 11, buf, &result.bitLength); - accumData = 0; - accumCount = 0; - } - } - if (accumCount > 0) // 1 character remaining - appendBitsToBuffer(accumData, 6, buf, &result.bitLength); - assert(result.bitLength == bitLen); - result.data = buf; - return result; -} - - -// Public function - see documentation comment in header file. -struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]) { - struct qrcodegen_Segment result; - result.mode = qrcodegen_Mode_ECI; - result.numChars = 0; - result.bitLength = 0; - if (assignVal < 0) - assert(false); - else if (assignVal < (1 << 7)) { - memset(buf, 0, 1 * sizeof(buf[0])); - appendBitsToBuffer(assignVal, 8, buf, &result.bitLength); - } else if (assignVal < (1 << 14)) { - memset(buf, 0, 2 * sizeof(buf[0])); - appendBitsToBuffer(2, 2, buf, &result.bitLength); - appendBitsToBuffer(assignVal, 14, buf, &result.bitLength); - } else if (assignVal < 1000000L) { - memset(buf, 0, 3 * sizeof(buf[0])); - appendBitsToBuffer(6, 3, buf, &result.bitLength); - appendBitsToBuffer(assignVal >> 10, 11, buf, &result.bitLength); - appendBitsToBuffer(assignVal & 0x3FF, 10, buf, &result.bitLength); - } else - assert(false); - result.data = buf; - return result; -} - - -// Calculates the number of bits needed to encode the given segments at the given version. -// Returns a non-negative number if successful. Otherwise returns -1 if a segment has too -// many characters to fit its length field, or the total bits exceeds INT16_MAX. -testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version) { - size_t i; - assert(segs != NULL || len == 0); - long result = 0; - for (i = 0; i < len; i++) { - int numChars = segs[i].numChars; - int bitLength = segs[i].bitLength; - assert(0 <= numChars && numChars <= INT16_MAX); - assert(0 <= bitLength && bitLength <= INT16_MAX); - int ccbits = numCharCountBits(segs[i].mode, version); - assert(0 <= ccbits && ccbits <= 16); - if (numChars >= (1L << ccbits)) - return -1; // The segment's length doesn't fit the field's bit width - result += 4L + ccbits + bitLength; - if (result > INT16_MAX) - return -1; // The sum might overflow an int type - } - assert(0 <= result && result <= INT16_MAX); - return (int)result; -} - - -// Returns the bit width of the character count field for a segment in the given mode -// in a QR Code at the given version number. The result is in the range [0, 16]. -static int numCharCountBits(enum qrcodegen_Mode mode, int version) { - assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); - int i = (version + 7) / 17; - switch (mode) { - case qrcodegen_Mode_NUMERIC : { static const int temp[] = {10, 12, 14}; return temp[i]; } - case qrcodegen_Mode_ALPHANUMERIC: { static const int temp[] = { 9, 11, 13}; return temp[i]; } - case qrcodegen_Mode_BYTE : { static const int temp[] = { 8, 16, 16}; return temp[i]; } - case qrcodegen_Mode_KANJI : { static const int temp[] = { 8, 10, 12}; return temp[i]; } - case qrcodegen_Mode_ECI : return 0; - default: assert(false); return -1; // Dummy value - } -} diff --git a/SSH/RPM/rublonPam-1.0/lib/qrcodegen.h b/SSH/RPM/rublonPam-1.0/lib/qrcodegen.h deleted file mode 100644 index e419ff6..0000000 --- a/SSH/RPM/rublonPam-1.0/lib/qrcodegen.h +++ /dev/null @@ -1,311 +0,0 @@ -/* - * QR Code generator library (C) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#pragma once - -#include -#include -#include - - -#ifdef __cplusplus -extern "C" { -#endif - - -/* - * This library creates QR Code symbols, which is a type of two-dimension barcode. - * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - * A QR Code structure is an immutable square grid of black and white cells. - * The library provides functions to create a QR Code from text or binary data. - * The library covers the QR Code Model 2 specification, supporting all versions (sizes) - * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. - * - * Ways to create a QR Code object: - * - High level: Take the payload data and call qrcodegen_encodeText() or qrcodegen_encodeBinary(). - * - Low level: Custom-make the list of segments and call - * qrcodegen_encodeSegments() or qrcodegen_encodeSegmentsAdvanced(). - * (Note that all ways require supplying the desired error correction level and various byte buffers.) - */ - - -/*---- Enum and struct types----*/ - -/* - * The error correction level in a QR Code symbol. - */ -enum qrcodegen_Ecc { - // Must be declared in ascending order of error protection - // so that an internal qrcodegen function works properly - qrcodegen_Ecc_LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords - qrcodegen_Ecc_MEDIUM , // The QR Code can tolerate about 15% erroneous codewords - qrcodegen_Ecc_QUARTILE, // The QR Code can tolerate about 25% erroneous codewords - qrcodegen_Ecc_HIGH , // The QR Code can tolerate about 30% erroneous codewords -}; - - -/* - * The mask pattern used in a QR Code symbol. - */ -enum qrcodegen_Mask { - // A special value to tell the QR Code encoder to - // automatically select an appropriate mask pattern - qrcodegen_Mask_AUTO = -1, - // The eight actual mask patterns - qrcodegen_Mask_0 = 0, - qrcodegen_Mask_1, - qrcodegen_Mask_2, - qrcodegen_Mask_3, - qrcodegen_Mask_4, - qrcodegen_Mask_5, - qrcodegen_Mask_6, - qrcodegen_Mask_7, -}; - - -/* - * Describes how a segment's data bits are interpreted. - */ -enum qrcodegen_Mode { - qrcodegen_Mode_NUMERIC = 0x1, - qrcodegen_Mode_ALPHANUMERIC = 0x2, - qrcodegen_Mode_BYTE = 0x4, - qrcodegen_Mode_KANJI = 0x8, - qrcodegen_Mode_ECI = 0x7, -}; - - -/* - * A segment of character/binary/control data in a QR Code symbol. - * The mid-level way to create a segment is to take the payload data - * and call a factory function such as qrcodegen_makeNumeric(). - * The low-level way to create a segment is to custom-make the bit buffer - * and initialize a qrcodegen_Segment struct with appropriate values. - * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. - * Any segment longer than this is meaningless for the purpose of generating QR Codes. - * Moreover, the maximum allowed bit length is 32767 because - * the largest QR Code (version 40) has 31329 modules. - */ -struct qrcodegen_Segment { - // The mode indicator of this segment. - enum qrcodegen_Mode mode; - - // The length of this segment's unencoded data. Measured in characters for - // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. - // Always zero or positive. Not the same as the data's bit length. - int numChars; - - // The data bits of this segment, packed in bitwise big endian. - // Can be null if the bit length is zero. - uint8_t *data; - - // The number of valid data bits used in the buffer. Requires - // 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8. - // The character count (numChars) must agree with the mode and the bit buffer length. - int bitLength; -}; - - - -/*---- Macro constants and functions ----*/ - -#define qrcodegen_VERSION_MIN 1 // The minimum version number supported in the QR Code Model 2 standard -#define qrcodegen_VERSION_MAX 40 // The maximum version number supported in the QR Code Model 2 standard - -// Calculates the number of bytes needed to store any QR Code up to and including the given version number, -// as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];' -// can store any single QR Code from version 1 to 25 (inclusive). The result fits in an int (or int16). -// Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX. -#define qrcodegen_BUFFER_LEN_FOR_VERSION(n) ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1) - -// The worst-case number of bytes needed to store one QR Code, up to and including -// version 40. This value equals 3918, which is just under 4 kilobytes. -// Use this more convenient value to avoid calculating tighter memory bounds for buffers. -#define qrcodegen_BUFFER_LEN_MAX qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX) - - - -/*---- Functions (high level) to generate QR Codes ----*/ - -/* - * Encodes the given text string to a QR Code, returning true if encoding succeeded. - * If the data is too long to fit in any version in the given range - * at the given ECC level, then false is returned. - * - The input text must be encoded in UTF-8 and contain no NULs. - * - The variables ecl and mask must correspond to enum constant values. - * - Requires 1 <= minVersion <= maxVersion <= 40. - * - The arrays tempBuffer and qrcode must each have a length - * of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion). - * - After the function returns, tempBuffer contains no useful data. - * - If successful, the resulting QR Code may use numeric, - * alphanumeric, or byte mode to encode the text. - * - In the most optimistic case, a QR Code at version 40 with low ECC - * can hold any UTF-8 string up to 2953 bytes, or any alphanumeric string - * up to 4296 characters, or any digit string up to 7089 characters. - * These numbers represent the hard upper limit of the QR Code standard. - * - Please consult the QR Code specification for information on - * data capacities per version, ECC level, and text encoding mode. - */ -bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], - enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); - - -/* - * Encodes the given binary data to a QR Code, returning true if encoding succeeded. - * If the data is too long to fit in any version in the given range - * at the given ECC level, then false is returned. - * - The input array range dataAndTemp[0 : dataLen] should normally be - * valid UTF-8 text, but is not required by the QR Code standard. - * - The variables ecl and mask must correspond to enum constant values. - * - Requires 1 <= minVersion <= maxVersion <= 40. - * - The arrays dataAndTemp and qrcode must each have a length - * of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion). - * - After the function returns, the contents of dataAndTemp may have changed, - * and does not represent useful data anymore. - * - If successful, the resulting QR Code will use byte mode to encode the data. - * - In the most optimistic case, a QR Code at version 40 with low ECC can hold any byte - * sequence up to length 2953. This is the hard upper limit of the QR Code standard. - * - Please consult the QR Code specification for information on - * data capacities per version, ECC level, and text encoding mode. - */ -bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], - enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); - - -/*---- Functions (low level) to generate QR Codes ----*/ - -/* - * Renders a QR Code representing the given segments at the given error correction level. - * The smallest possible QR Code version is automatically chosen for the output. Returns true if - * QR Code creation succeeded, or false if the data is too long to fit in any version. The ECC level - * of the result may be higher than the ecl argument if it can be done without increasing the version. - * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and byte) to encode text in less space. - * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). - * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will - * result in them being clobbered, but the QR Code output will still be correct. - * But the qrcode array must not overlap tempBuffer or any segment's data buffer. - */ -bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, - enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]); - - -/* - * Renders a QR Code representing the given segments with the given encoding parameters. - * Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions. - * The smallest possible QR Code version within the given range is automatically - * chosen for the output. Iff boostEcl is true, then the ECC level of the result - * may be higher than the ecl argument if it can be done without increasing the - * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or - * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow). - * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and byte) to encode text in less space. - * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). - * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will - * result in them being clobbered, but the QR Code output will still be correct. - * But the qrcode array must not overlap tempBuffer or any segment's data buffer. - */ -bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, - int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); - - -/* - * Tests whether the given string can be encoded as a segment in alphanumeric mode. - * A string is encodable iff each character is in the following set: 0 to 9, A to Z - * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. - */ -bool qrcodegen_isAlphanumeric(const char *text); - - -/* - * Tests whether the given string can be encoded as a segment in numeric mode. - * A string is encodable iff each character is in the range 0 to 9. - */ -bool qrcodegen_isNumeric(const char *text); - - -/* - * Returns the number of bytes (uint8_t) needed for the data buffer of a segment - * containing the given number of characters using the given mode. Notes: - * - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or - * the number of needed bits exceeds INT16_MAX (i.e. 32767). - * - Otherwise, all valid results are in the range [0, ceil(INT16_MAX / 8)], i.e. at most 4096. - * - It is okay for the user to allocate more bytes for the buffer than needed. - * - For byte mode, numChars measures the number of bytes, not Unicode code points. - * - For ECI mode, numChars must be 0, and the worst-case number of bytes is returned. - * An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. - */ -size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars); - - -/* - * Returns a segment representing the given binary data encoded in - * byte mode. All input byte arrays are acceptable. Any text string - * can be converted to UTF-8 bytes and encoded as a byte mode segment. - */ -struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]); - - -/* - * Returns a segment representing the given string of decimal digits encoded in numeric mode. - */ -struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]); - - -/* - * Returns a segment representing the given text string encoded in alphanumeric mode. - * The characters allowed are: 0 to 9, A to Z (uppercase only), space, - * dollar, percent, asterisk, plus, hyphen, period, slash, colon. - */ -struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]); - - -/* - * Returns a segment representing an Extended Channel Interpretation - * (ECI) designator with the given assignment value. - */ -struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); - - -/*---- Functions to extract raw data from QR Codes ----*/ - -/* - * Returns the side length of the given QR Code, assuming that encoding succeeded. - * The result is in the range [21, 177]. Note that the length of the array buffer - * is related to the side length - every 'uint8_t qrcode[]' must have length at least - * qrcodegen_BUFFER_LEN_FOR_VERSION(version), which equals ceil(size^2 / 8 + 1). - */ -int qrcodegen_getSize(const uint8_t qrcode[]); - - -/* - * Returns the color of the module (pixel) at the given coordinates, which is false - * for white or true for black. The top left corner has the coordinates (x=0, y=0). - * If the given coordinates are out of bounds, then false (white) is returned. - */ -bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y); - - -#ifdef __cplusplus -} -#endif diff --git a/SSH/RPM/rublonPam-1.0/login_rublon.te b/SSH/RPM/rublonPam-1.0/login_rublon.te deleted file mode 100644 index de46001..0000000 --- a/SSH/RPM/rublonPam-1.0/login_rublon.te +++ /dev/null @@ -1,10 +0,0 @@ -module login_rublon 1.0; - -require { -type http_port_t; -type http_cache_port_t; -type sshd_t; -class tcp_socket name_connect; -}; - -allow sshd_t {http_port_t http_cache_port_t}:tcp_socket name_connect; diff --git a/SSH/RPM/rublonPam-1.0/rublonPam.c b/SSH/RPM/rublonPam-1.0/rublonPam.c deleted file mode 100644 index 2a85cde..0000000 --- a/SSH/RPM/rublonPam-1.0/rublonPam.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "src/misc.h" -#include -#include -#include -#include -#include -#include -#include - -void displayHeader(pam_handle_t *pamh) { - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "+++++. `;+++++++++++++++: ++++++++` `+++++++++++++++++++++++++++++"); - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "+++++` ++++++++++++++: ++++++++` `+++++++++++++++++++++++++++++"); - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "+++++` +++++++++++++: ++++++++` `+++++++++++++++++++++++++++++"); - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "+++++` ''` ++';;'++;;;++: +:,+++++` `++++++.`.+++++;;;'+..++++++++"); - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "+++++` +++ ++ `++ ++: +++` `++++ `+++ ` .++++++"); - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "+++++` ++: ++ `++ ++: ++` `+++ `++ ++++++"); - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "+++++` `++ `++ ++: `' ++` `+++ `' ++ +: ++++++"); - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "+++++` +++ `++ ++: +++ ;+` `++, +++ '+ .++ ++++++"); - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "+++++` ` .+++ `++ ++: +++ ;+` `++, +++ '+ .++ ++++++"); - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "+++++` +. +++` ,` ++: `; ++` +++ `; ++ .++ ++++++"); - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "+++++` ++ +++ ++: ++, ++ .++ .++ ++++++"); - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "+++++` +++ `++, +++: ++++ +++ .+++ .++ ++++++"); - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "++++++++++++++++++++;.,++++++++++;:+++++++;.,+++++.`,++++++++++++++++++++"); - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); -} - -PAM_EXTERN int pam_sm_setcred( pam_handle_t *pamh, int flags, int argc, const char **argv ) { - return PAM_SUCCESS; -} - -PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) { - return PAM_SUCCESS; -} - -PAM_EXTERN int pam_sm_authenticate( pam_handle_t *pamh, int flags,int argc, const char **argv ) { - displayHeader(pamh); - - int access = startRublon(pamh); - - if(access == STATUS_BYPASS){ - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "User bypassed"); - return PAM_SUCCESS; - } - if(access == STATUS_DENIED){ - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "Access denied!"); - return PAM_MAXTRIES; - } - if(access == STATUS_PENDING){ - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "Status pending...!"); - return PAM_SUCCESS; - } - if(access == CONNECTION_ERROR){ - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "Connection error"); - return PAM_MAXTRIES; - } - if(access == STATUS_UNKNOWN){ - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "Connection error : status unknown"); - return PAM_MAXTRIES; - } - if(access == STATUS_CONFIRMED) { - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "Access confirmed by Rublon 2FA!"); - return PAM_SUCCESS; - } - - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "End PAM module"); - - return PAM_MAXTRIES; -} \ No newline at end of file diff --git a/SSH/RPM/rublonPam-1.0/src/coreHandler.c b/SSH/RPM/rublonPam-1.0/src/coreHandler.c deleted file mode 100644 index 3a945cd..0000000 --- a/SSH/RPM/rublonPam-1.0/src/coreHandler.c +++ /dev/null @@ -1,398 +0,0 @@ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE 1 -#endif - -#include "../lib/cJSON.h" -#include "../lib/qrcodegen.h" -#include "misc.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -struct MemoryStruct { - char *memory; - size_t size; -}; - -static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { - size_t realsize = size * nmemb; - struct MemoryStruct *mem = (struct MemoryStruct *)userp; - mem->memory = realloc(mem->memory, mem->size + realsize + 1); - if(mem->memory == NULL) - return 0; - memcpy(&(mem->memory[mem->size]), contents, realsize); - mem->size += realsize; - mem->memory[mem->size] = 0; - return realsize; -} - -char *parseNestedJson(char source[], char name[], char indexName[]) { - const cJSON *value = NULL; - const cJSON *nestedValue = NULL; - cJSON *monitor_json = cJSON_Parse(source); - value = cJSON_GetObjectItemCaseSensitive(monitor_json, name); - nestedValue = cJSON_GetObjectItemCaseSensitive(value,indexName); - if(nestedValue) - return nestedValue->valuestring; - else - return NULL; -} - -int parseNestedJsonInt(char source[], char name[], char indexName[]) { - const cJSON *value = NULL; - const cJSON *nestedValue = NULL; - cJSON *monitor_json = cJSON_Parse(source); - value = cJSON_GetObjectItemCaseSensitive(monitor_json, name); - nestedValue = cJSON_GetObjectItemCaseSensitive(value,indexName); - if(nestedValue) - return nestedValue->valueint; - else - return 0; -} - -char *parseJson(char source[], char name[] ) { - const cJSON *value = NULL; - cJSON *monitor_json = cJSON_Parse(source); - value = cJSON_GetObjectItemCaseSensitive(monitor_json, name); - if(value) - return value->valuestring; - else - return NULL; -} - -void printQr(const uint8_t qrcode[],pam_handle_t *pamh) { - char *qrCodeString = ""; - int size = qrcodegen_getSize(qrcode); - int border = 1; - int y; - for(y = -border; y < size + border; y++) { - int x; - for(x = -border; x < size + border; x++) - asprintf(&qrCodeString,"%s%s",qrCodeString,qrcodegen_getModule(qrcode, x, y) ? " " : "\u2588\u2588"); - asprintf(&qrCodeString,"%s%s",qrCodeString,"\n"); - } - asprintf(&qrCodeString,"%s%s",qrCodeString,"\n"); - - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "%s",qrCodeString); -} - -void displayQrCode(pam_handle_t *pamh, char *qrToken) { - enum qrcodegen_Ecc errCorLvl = qrcodegen_Ecc_LOW; - uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; - uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; - bool ok = qrcodegen_encodeText(qrToken, tempBuffer, qrcode, errCorLvl, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); - if(ok) - printQr(qrcode, pamh); -} - -void displayAvailableAuthenticationMethods(cJSON *methods, pam_handle_t *pamh) { - - if(cJSON_GetArraySize(methods)) { - int i; - for (i = 0; i < cJSON_GetArraySize(methods); i++) { - if (strcmp(cJSON_GetArrayItem(methods, i)->valuestring,"email") == 0) - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "%d: Email verification link",i+1); - if (strcmp(cJSON_GetArrayItem(methods, i)->valuestring,"qrcode") == 0) - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "%d: QR code (Rublon app required)",i+1); - if (strcmp(cJSON_GetArrayItem(methods, i)->valuestring,"push") == 0) - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "%d: Push notification (Rublon app required)",i+1); - if (strcmp(cJSON_GetArrayItem(methods, i)->valuestring,"totp") == 0) - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "%d: TOTP code (Rublon app required)",i+1); - if (strcmp(cJSON_GetArrayItem(methods, i)->valuestring,"sms") == 0) - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "%d: SMS code",i+1); - } - } -} - -void selectMethodModule(pam_handle_t *pamh, cJSON *methods, char **selectedMethod) { - char *authentication = 0; - int idx = 0; - if(cJSON_GetArraySize(methods)) { - displayAvailableAuthenticationMethods(methods, pamh); - do{ - if(cJSON_GetArraySize(methods) == 1) { - idx=1; - break; - } - pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &authentication, "Select method [1-%d]: ",cJSON_GetArraySize(methods)); - idx = atoi(authentication); - }while(idx < 1 || idx > cJSON_GetArraySize(methods)); - *selectedMethod=cJSON_GetArrayItem(methods, idx-1)->valuestring; - } -} - -void displaySelectedMethodMsg(pam_handle_t *pamh, char *selectedMethod) { - if(strcmp(selectedMethod,"email") == 0) - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "\nWe have sent a verification link.\nUse this link to sing in to your account."); - else if(strcmp(selectedMethod,"qrcode") == 0) - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "\nSign in by scanning QR code (Rublon app required)"); - else if(strcmp(selectedMethod,"push") == 0) - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "\nSign in using Push notification (Rublon app required)"); -} - -bool transactionErrorException(pam_handle_t *pamh, char *curlResponse) { - char *exception = NULL; - exception = parseNestedJson(curlResponse,"result","exception"); - if(exception != NULL) { - pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,NULL, "Rublon exception: %s",exception); - return true; - } - return false; -} - -char *curlHandler(pam_handle_t *pamh, char *jsonObj, char *url, char *secretKey) { - CURL *curl; - CURLcode res, headerLength; - struct MemoryStruct chunks; - chunks.memory = malloc(1); - chunks.size = 0; - curl = curl_easy_init(); - char *curlResponse, *responseXRublon; - char *xRublon; - - asprintf(&xRublon,"X-Rublon-Signature: %s",signData(pamh,jsonObj, secretKey)); - - if(curl) { - struct curl_slist *chunk = NULL; - curl_easy_setopt(curl, CURLOPT_URL, url); - chunk = curl_slist_append(chunk, "Content-Type: application/json"); - chunk = curl_slist_append(chunk, "Accept: application/json"); - chunk = curl_slist_append(chunk, xRublon); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonObj); - curl_easy_setopt(curl, CURLOPT_HEADER, 1); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunks); - res = curl_easy_perform(curl); - if(res != CURLE_OK) - return NULL; - long size; - curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &size); - curlResponse = curlResponseJsonParser(pamh, size, chunks.memory); - responseXRublon = curlResponseSignatureParser(pamh, size, chunks.memory); - if(!verifyData(pamh, curlResponse, secretKey, responseXRublon)) { - pam_prompt(pamh, PAM_TEXT_INFO, NULL, "Invalid Signature Exception"); - return NULL; - } - curl_easy_cleanup(curl); - if(chunks.memory) - free(chunks.memory); - return curlResponse; - } - else - return NULL; -} - -void curlHandlerThread(pam_handle_t *pamh, char *jsonObj, char *url, char *secretKey, char **accessToken, char **status, char **exception) { - char *curlResponse = curlHandler(pamh, jsonObj, url, secretKey); - if(curlResponse != NULL) { - *status = parseJson(curlResponse,"status"); - *exception = parseNestedJson(curlResponse,"result","exception"); - *accessToken = parseNestedJson(curlResponse,"result","token"); - } - else - *status = "ERROR"; -} - -bool checkResult(char *curlResponse) { - cJSON *json = cJSON_Parse(curlResponse); - cJSON *result_json = cJSON_GetObjectItemCaseSensitive(json, "result"); - cJSON_Delete(json); - if (cJSON_IsTrue(result_json) == 1) - return true; - return false; -} - -bool isDigit(char *code) { - int i; - for (i=0;i -#include "../lib/cJSON.h" -#include -#include -#include -#include -#include - -#include - -#define STATUS_PENDING 0 -#define STATUS_WAITING 1 -#define STATUS_DENIED 2 -#define STATUS_BYPASS 3 -#define STATUS_UNKNOWN 4 -#define STATUS_CONFIRMED 5 -#define CONNECTION_ERROR 6 -#define CHANGE_METHOD 7 - -#define TOTP_SMS_INPUT_CODE_SIZE 6 -#define SIGNATURE_SIZE 64 -bool isOneOfSelectedMethods(char *selectedMethod, char *methods[], int methodsSize); -int startRublon(pam_handle_t *pamh); -void curlHandlerThread(pam_handle_t *pamh, char *jsonObj, char *url, char *secretKey, char **accessToken, char **status, char **exception); -int postInit(pam_handle_t *pamh, cJSON **availableMethods, char **transactionId, char *systemToken, char *secretKey, const char *appUserId, char *userEmail, char *rublonApiServer); -int postMethod(pam_handle_t *pamh, char *secretKey, char *tId, char *selectedMethod, char *rublonApiServer, char *systemToken, bool onlyOneMethod); -int postConfirmCode(pam_handle_t *pamh, char *secretKey, char *systemToken, char *transactionId, char *selectedMethod, char *rublonApiServer, bool onlyOneMethod); - -void selectMethodModule(pam_handle_t *pamh, cJSON *methods, char **selectedMethod); -void substring(char s[], char sub[], int p, int l); -char *curlResponseJsonParser(pam_handle_t *pamh, long headerLength, char *resp); -char *curlResponseSignatureParser(pam_handle_t *pamh, long headerLength, char *resp); -char *signData(pam_handle_t *pamh, char *data, char *secretKey); -int verifyData(pam_handle_t *pamh, char *data, char *secretKey, char *sign); -int postCredentials(pam_handle_t *pamh, char *systemToken, char *accessToken, char *rublonApiServer, char *secretKey); \ No newline at end of file diff --git a/SSH/RPM/rublonPam-1.0/src/pamApp.c b/SSH/RPM/rublonPam-1.0/src/pamApp.c deleted file mode 100644 index 708de2d..0000000 --- a/SSH/RPM/rublonPam-1.0/src/pamApp.c +++ /dev/null @@ -1,152 +0,0 @@ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE 1 -#endif - -#include "misc.h" -#include "../lib/cfg_parse.h" -#include "../lib/cJSON.h" -#include -#include -#include -#include -#include -#include -#include - -struct args { - int listenerStatus; - char *transactionId; - char *systemToken; - char *secretKey; - char *rublonApiServer; - char *accessToken; - char *status; - char *exception; - pam_handle_t *pamh; -}; - -pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - -void* listenerThread(void* input) { - char *jsonObj; - char *url; - char *status; - char *accessToken; - char *exception; - - do { - pthread_mutex_lock(&mutex); - asprintf(&jsonObj,"{\"tid\":\"%s\",\"systemToken\":\"%s\"}",((struct args*)input)->transactionId, ((struct args*)input)->systemToken); - asprintf(&url,"%s/api/transaction/listener",((struct args*)input)->rublonApiServer); - status = ((struct args*)input)->status; - accessToken = ((struct args*)input)->accessToken; - exception = ((struct args*)input)->exception; - curlHandlerThread(((struct args*)input)->pamh, jsonObj, url, ((struct args*)input)->secretKey, &accessToken, &status, &exception); - ((struct args*)input)->status = status; - ((struct args*)input)->accessToken = accessToken; - ((struct args*)input)->exception = exception; - if(strcmp(status,"ERROR") == 0 || exception != NULL) { - ((struct args*)input)->accessToken = ""; - ((struct args*)input)->listenerStatus = STATUS_DENIED; - } - else - ((struct args*)input)->listenerStatus = STATUS_PENDING; - pthread_mutex_unlock(&mutex); - sleep(5); - } while (((struct args*)input)->accessToken == NULL); - pthread_exit(0); -} - -char *getConfigValue(const char * value) { - struct cfg_struct* cfg; - cfg = cfg_init(); - if (cfg_load(cfg,"/lib64/security/.config") < 0) - return NULL; - else - return (char*)cfg_get(cfg,value); -} - -int startRublon(pam_handle_t *pamh) { - char *systemToken; - char *secretKey; - const char *appUserId; - char *userEmail; - char *userDomain; - char *rublonApiServer; - char *transactionId; - const char *pamUser; - char *selectedMethod = NULL; - char *accessToken = NULL; - cJSON *availableMethods; - bool codeRequired = false; - - struct args *threadArgs = (struct args *)malloc(sizeof(struct args)); - pam_get_user(pamh, &pamUser, NULL); - - systemToken = getConfigValue("systemToken"); - secretKey = getConfigValue("secretKey"); - appUserId = pamUser; - userEmail = getConfigValue("userEmail"); - userDomain = getConfigValue("userDomain"); - rublonApiServer = getConfigValue("rublonApiServer"); - asprintf(&userEmail, "%s%s%s", pamUser,"@",userDomain); - transactionId = NULL; - - int initStatus = postInit(pamh, &availableMethods, &transactionId , systemToken, secretKey, appUserId, userEmail, rublonApiServer); - if(initStatus != STATUS_PENDING) - return initStatus; - - threadArgs->pamh = pamh; - threadArgs->transactionId = transactionId; - threadArgs->systemToken = systemToken; - threadArgs->secretKey = secretKey; - threadArgs->rublonApiServer = rublonApiServer; - threadArgs->accessToken = accessToken; - threadArgs->status = NULL; - threadArgs->exception = NULL; - threadArgs->listenerStatus = STATUS_PENDING; - - pthread_t listener; - pthread_create(&listener,NULL,listenerThread, threadArgs); - - bool changeMethod; - do { - bool onlyOneMethod = false; - selectMethodModule(pamh, availableMethods, &selectedMethod); - if(cJSON_GetArraySize(availableMethods) == 1) - onlyOneMethod = true; - changeMethod = false; - int methodStatus = postMethod(pamh, secretKey, transactionId, selectedMethod, rublonApiServer, systemToken, onlyOneMethod); - if(methodStatus == CHANGE_METHOD) { - changeMethod = true; - continue; - } - else if(methodStatus != STATUS_PENDING) - return methodStatus; - - char *methodsToCompare[] = {"totp","sms"}; - - if(isOneOfSelectedMethods(selectedMethod, methodsToCompare, 2)) { - int confirmCodeStatus = postConfirmCode(pamh, secretKey, systemToken, transactionId, selectedMethod, rublonApiServer, onlyOneMethod); - if(confirmCodeStatus == CHANGE_METHOD) { - changeMethod = true; - continue; - } - else if(confirmCodeStatus != STATUS_PENDING) - return confirmCodeStatus; - continue; - } - - }while(changeMethod == true); - do { - sleep(1); - }while(threadArgs->accessToken == NULL); - - pthread_mutex_destroy(&mutex); - - if(threadArgs->listenerStatus != STATUS_PENDING) - return threadArgs->listenerStatus; - - int credentialsStatus = postCredentials(pamh, systemToken, threadArgs->accessToken, rublonApiServer, secretKey); - return credentialsStatus; -} \ No newline at end of file diff --git a/SSH/RPM/rublonPam-1.0/src/signatureWrapper.c b/SSH/RPM/rublonPam-1.0/src/signatureWrapper.c deleted file mode 100644 index a0fc6d7..0000000 --- a/SSH/RPM/rublonPam-1.0/src/signatureWrapper.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "misc.h" -#include -#include -#include -#include -#include -#include -#include -#include - -void substring(char s[], char sub[], int p, int l) { - int c = 0; - - while (c < l) { - sub[c] = s[p+c-1]; - c++; - } - sub[c] = '\0'; -} - -char *curlResponseJsonParser(pam_handle_t *pamh, long headerLength, char *resp) { - char *response = malloc (sizeof (char) * strlen(resp)); - substring(resp, response, headerLength+1, strlen(resp)-headerLength+1); - return response; -} - -char *curlResponseSignatureParser(pam_handle_t *pamh, long headerLength, char *resp) { - char *rublonHeader = "X-Rublon-Signature: "; - char *headers = malloc (sizeof (char) * headerLength); - - substring(resp, headers, 1, headerLength); - const char * curLine = headers; - while(curLine) { - const char * nextLine = strchr(curLine, '\n'); - int curLineLen = nextLine ? (nextLine-curLine) : strlen(curLine); - char * tempStr = (char *) malloc(curLineLen+1); - if (tempStr) { - memcpy(tempStr, curLine, curLineLen); - tempStr[curLineLen] = '\0'; - char *signatureExist = strstr(tempStr, rublonHeader); - if(signatureExist != NULL) - substring(tempStr, headers, strlen(rublonHeader) +1, SIGNATURE_SIZE); - free(tempStr); - } - curLine = nextLine ? (nextLine+1) : NULL; - } - return headers; -} - -char *signData(pam_handle_t *pamh, char *data, char *secretKey) { - unsigned char *hmac = HMAC(EVP_sha256(), (unsigned char*)secretKey, strlen(secretKey), (unsigned char*)data, strlen(data), NULL, NULL); - char *xRublon = malloc (sizeof (char) * 64); - int i; - for(i = 0; i < 32; i++) - sprintf(&xRublon[i*2], "%02x", (unsigned int)hmac[i]); - return xRublon; -} - -int verifyData(pam_handle_t *pamh, char *data, char *secretKey, char *sign) { - char *dataSign = signData(pamh, data, secretKey); - return (strcmp(dataSign,sign) == 0); -} \ No newline at end of file diff --git a/SSH/RPM/rublonPam.spec b/SSH/RPM/rublonPam.spec deleted file mode 100644 index 108e4e6..0000000 --- a/SSH/RPM/rublonPam.spec +++ /dev/null @@ -1,43 +0,0 @@ -Summary: Rublon 2FA for Linux SSH -Name: rublonPam -Version: 1.0 -Release: 1 -License: - -Group: 2FA -Source: rublonPam.tar.gz -URL: https://rublon.net -Distribution: CENTOS Linux -Packager: KRI - -Requires: gcc -Requires: make -Requires: curl-devel -Requires: openssl-devel -Requires: pam-devel -Requires: policycoreutils-python - -%description -Rublon 2FA - -%prep - -%setup -q - -%install -rm -rf $RPM_BUILD_ROOT -make -install -d $RPM_BUILD_ROOT/lib64/security -install rublonPam.so $RPM_BUILD_ROOT/lib64/security/rublonPam.so -install .config $RPM_BUILD_ROOT/lib64/security/.config -install login_rublon.te $RPM_BUILD_ROOT/lib64/security/login_rublon.te - -%files -/lib64/security/.config -/lib64/security/rublonPam.so -/lib64/security/login_rublon.te - -%post -sudo checkmodule -M -m -o /lib64/security/login_rublon.mod /lib64/security/login_rublon.te -sudo semodule_package -o /lib64/security/login_rublon.pp -m /lib64/security/login_rublon.mod -sudo semodule -i /lib64/security/login_rublon.pp -service sshd restart diff --git a/SSH/RPM/rublonPam.tar.gz b/SSH/RPM/rublonPam.tar.gz deleted file mode 100644 index 852e77c747e8e4d264a20c81d62c6680d199324b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43049 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 N{}1z8yJ`R+0|1VD