initial commit
This commit is contained in:
commit
22dc118616
17 files changed
+33251
No files matched your search
@@ -0,0 +1,5 @@
|
||||
*.o
|
||||
.vscode
|
||||
.cache
|
||||
.idea
|
||||
build
|
||||
@@ -0,0 +1,16 @@
|
||||
cmake_minimum_required (VERSION 3.6)
|
||||
project(fmongoose LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 23)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
include_directories(./src/lib/)
|
||||
|
||||
FILE(GLOB_RECURSE SOURCES src/*.c)
|
||||
|
||||
add_executable(
|
||||
fmongoose
|
||||
${SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(fmongoose ssl crypto)
|
||||
@@ -0,0 +1,21 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -g -std=gnu23
|
||||
LDFLAGS =
|
||||
LIBS = -lpthread -lcrypto
|
||||
INCLUDES = -I./src/lib -I./src
|
||||
SRCS = $(wildcard src/*.c src/lib/*.c)
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
TARGET = fmongoose
|
||||
BUILD_DIR=./build
|
||||
CROSS_COMPILE =
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CROSS_COMPILE)$(CC) $(LDFLAGS) $^ -o $@ $(LIBS)
|
||||
|
||||
%.o: %.c
|
||||
$(CROSS_COMPILE)$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET)
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
project('fmongoose', 'c', default_options : ['c_std=gnu23'])
|
||||
include_dirs = include_directories('src', 'src/lib')
|
||||
deps = dependency('openssl')
|
||||
executable(
|
||||
'fmongoose',
|
||||
'src/main.c',
|
||||
'src/user.c',
|
||||
'src/utils.c',
|
||||
'src/jwt.c',
|
||||
'src/lib/mongoose.c',
|
||||
'src/lib/cJSON.c',
|
||||
include_directories: include_dirs,
|
||||
dependencies: deps,
|
||||
)
|
||||
@@ -0,0 +1,268 @@
|
||||
#include "jwt.h"
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/buffer.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <string.h>
|
||||
|
||||
// Удаляет символы '=' в конце строки (падинг)
|
||||
static void remove_padding(const char *str) {
|
||||
char *pos = strchr(str, '\0');
|
||||
while (pos > str && *(pos - 1) == '=') {
|
||||
*(--pos) = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
// Преобразует Base64 в Base64URL
|
||||
static void base64_to_base64url(char *str) {
|
||||
for (char *p = str; *p; p++) {
|
||||
if (*p == '+')
|
||||
*p = '-';
|
||||
else if (*p == '/')
|
||||
*p = '_';
|
||||
}
|
||||
remove_padding(str); // Удаляем паддинг
|
||||
}
|
||||
|
||||
// Преобразует Base64URL в Base64
|
||||
static void base64url_to_base64(char *str) {
|
||||
for (char *p = str; *p; p++) {
|
||||
if (*p == '-')
|
||||
*p = '+';
|
||||
else if (*p == '_')
|
||||
*p = '/';
|
||||
}
|
||||
// Добавляем паддинг для кратности 4
|
||||
size_t len = strlen(str);
|
||||
size_t pad = (4 - (len % 4)) % 4;
|
||||
if (pad) {
|
||||
char *end = str + len;
|
||||
for (size_t i = 0; i < pad; i++) {
|
||||
*(end++) = '=';
|
||||
}
|
||||
*end = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
// Base64URL кодирование
|
||||
char *jwt_base64url_encode(const unsigned char *input, const int length) {
|
||||
BIO *b64 = BIO_new(BIO_f_base64());
|
||||
BIO *bio = BIO_new(BIO_s_mem());
|
||||
bio = BIO_push(b64, bio);
|
||||
|
||||
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
|
||||
BIO_write(bio, input, length);
|
||||
BIO_flush(bio);
|
||||
|
||||
BUF_MEM *bufferPtr;
|
||||
BIO_get_mem_ptr(bio, &bufferPtr);
|
||||
|
||||
const auto result = (char *)malloc(bufferPtr->length + 1);
|
||||
memcpy(result, bufferPtr->data, bufferPtr->length);
|
||||
result[bufferPtr->length] = '\0';
|
||||
|
||||
BIO_free_all(bio);
|
||||
|
||||
base64_to_base64url(result); // Преобразование в Base64URL
|
||||
return result;
|
||||
}
|
||||
|
||||
// Base64URL декодирование
|
||||
unsigned char *jwt_base64url_decode(const char *input, int input_len,
|
||||
int *output_len) {
|
||||
if (input_len == 0)
|
||||
input_len = strlen(input);
|
||||
|
||||
// Создаем копию для преобразования
|
||||
const auto input_copy = (char *)malloc(input_len + 1);
|
||||
strncpy(input_copy, input, input_len);
|
||||
input_copy[input_len] = '\0';
|
||||
|
||||
base64url_to_base64(input_copy); // Преобразование в Base64
|
||||
|
||||
BIO *b64 = BIO_new(BIO_f_base64());
|
||||
BIO *bio = BIO_new_mem_buf(input_copy, -1);
|
||||
bio = BIO_push(b64, bio);
|
||||
|
||||
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
|
||||
|
||||
const auto buffer = (unsigned char *)malloc(input_len);
|
||||
const int len = BIO_read(bio, buffer, input_len);
|
||||
|
||||
BIO_free_all(bio);
|
||||
free(input_copy);
|
||||
|
||||
if (len <= 0) {
|
||||
free(buffer);
|
||||
*output_len = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*output_len = len;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Создание JWT токена
|
||||
char *jwt_createJWT(const char *header_json, const char *payload_json,
|
||||
const char *secret) {
|
||||
char *encodedHeader = jwt_base64url_encode((const unsigned char *)header_json,
|
||||
strlen(header_json));
|
||||
char *encodedPayload = jwt_base64url_encode(
|
||||
(const unsigned char *)payload_json, strlen(payload_json));
|
||||
|
||||
char *unsignedToken =
|
||||
(char *)malloc(strlen(encodedHeader) + strlen(encodedPayload) + 2);
|
||||
sprintf(unsignedToken, "%s.%s", encodedHeader, encodedPayload);
|
||||
|
||||
unsigned int digest_len;
|
||||
unsigned char *digest = jwt_hmac_sha256(secret, strlen(secret), unsignedToken,
|
||||
strlen(unsignedToken), &digest_len);
|
||||
char *encodedSignature = jwt_base64url_encode(digest, digest_len);
|
||||
|
||||
char *jwt =
|
||||
(char *)malloc(strlen(unsignedToken) + strlen(encodedSignature) + 2);
|
||||
sprintf(jwt, "%s.%s", unsignedToken, encodedSignature);
|
||||
|
||||
free(encodedHeader);
|
||||
free(encodedPayload);
|
||||
free(unsignedToken);
|
||||
free(digest);
|
||||
free(encodedSignature);
|
||||
|
||||
return jwt;
|
||||
}
|
||||
|
||||
// Генерация HMAC-SHA256
|
||||
unsigned char *jwt_hmac_sha256(const char *key, int key_len, const char *data,
|
||||
int data_len, unsigned int *digest_len) {
|
||||
unsigned char *digest = (unsigned char *)malloc(EVP_MAX_MD_SIZE);
|
||||
HMAC(EVP_sha256(), key, key_len, (const unsigned char *)data, data_len,
|
||||
digest, digest_len);
|
||||
return digest;
|
||||
}
|
||||
|
||||
// Верификация JWT токена
|
||||
bool jwt_verifyJWT(const char *token, const char *secret) {
|
||||
const char *dot1 = strchr(token, '.');
|
||||
const char *dot2 = dot1 ? strchr(dot1 + 1, '.') : nullptr;
|
||||
if (!dot1 || !dot2)
|
||||
return false;
|
||||
|
||||
size_t header_len = dot1 - token;
|
||||
size_t payload_len = dot2 - dot1 - 1;
|
||||
size_t sig_len = strlen(dot2 + 1);
|
||||
|
||||
char *header = (char *)malloc(header_len + 1);
|
||||
strncpy(header, token, header_len);
|
||||
header[header_len] = '\0';
|
||||
|
||||
char *payload = (char *)malloc(payload_len + 1);
|
||||
strncpy(payload, dot1 + 1, payload_len);
|
||||
payload[payload_len] = '\0';
|
||||
|
||||
char *signature = strndup(dot2 + 1, sig_len);
|
||||
|
||||
char *signing_input = (char *)malloc(header_len + payload_len + 2);
|
||||
sprintf(signing_input, "%s.%s", header, payload);
|
||||
|
||||
int expected_sig_len;
|
||||
unsigned char *expected_sig_bin =
|
||||
jwt_base64url_decode(signature, sig_len, &expected_sig_len);
|
||||
|
||||
unsigned int calculated_sig_len;
|
||||
unsigned char *calculated_sig =
|
||||
jwt_hmac_sha256(secret, strlen(secret), signing_input,
|
||||
strlen(signing_input), &calculated_sig_len);
|
||||
|
||||
bool sig_valid =
|
||||
(calculated_sig_len == expected_sig_len) &&
|
||||
(memcmp(calculated_sig, expected_sig_bin, calculated_sig_len) == 0);
|
||||
|
||||
bool exp_valid = true;
|
||||
if (sig_valid) {
|
||||
int payload_bin_len;
|
||||
unsigned char *payload_bin =
|
||||
jwt_base64url_decode(payload, payload_len, &payload_bin_len);
|
||||
char *payload_str = (char *)malloc(payload_bin_len + 1);
|
||||
memcpy(payload_str, payload_bin, payload_bin_len);
|
||||
payload_str[payload_bin_len] = '\0';
|
||||
|
||||
cJSON *json_payload = cJSON_Parse(payload_str);
|
||||
if (json_payload) {
|
||||
cJSON *exp = cJSON_GetObjectItemCaseSensitive(json_payload, "exp");
|
||||
if (cJSON_IsNumber(exp)) {
|
||||
time_t now = time(nullptr);
|
||||
if (now >= (time_t)exp->valuedouble) {
|
||||
exp_valid = false;
|
||||
}
|
||||
}
|
||||
cJSON_Delete(json_payload);
|
||||
}
|
||||
free(payload_bin);
|
||||
free(payload_str);
|
||||
}
|
||||
|
||||
free(header);
|
||||
free(payload);
|
||||
free(signature);
|
||||
free(signing_input);
|
||||
free(expected_sig_bin);
|
||||
free(calculated_sig);
|
||||
|
||||
return sig_valid && exp_valid;
|
||||
}
|
||||
|
||||
// Получение payload из токена
|
||||
cJSON *jwt_get_payload(const char *token) {
|
||||
const char *dot1 = strchr(token, '.');
|
||||
const char *dot2 = dot1 ? strchr(dot1 + 1, '.') : nullptr;
|
||||
if (!dot1 || !dot2)
|
||||
return nullptr;
|
||||
|
||||
size_t payload_len = dot2 - dot1 - 1;
|
||||
char *payload = (char *)malloc(payload_len + 1);
|
||||
strncpy(payload, dot1 + 1, payload_len);
|
||||
payload[payload_len] = '\0';
|
||||
|
||||
int payload_bin_len;
|
||||
unsigned char *payload_bin =
|
||||
jwt_base64url_decode(payload, payload_len, &payload_bin_len);
|
||||
char *payload_str = (char *)malloc(payload_bin_len + 1);
|
||||
memcpy(payload_str, payload_bin, payload_bin_len);
|
||||
payload_str[payload_bin_len] = '\0';
|
||||
|
||||
cJSON *json_payload = cJSON_Parse(payload_str);
|
||||
|
||||
free(payload);
|
||||
free(payload_bin);
|
||||
free(payload_str);
|
||||
|
||||
return json_payload;
|
||||
}
|
||||
|
||||
// Получение header из токена
|
||||
cJSON *jwt_get_header(const char *token) {
|
||||
const char *dot1 = strchr(token, '.');
|
||||
if (!dot1)
|
||||
return nullptr;
|
||||
|
||||
size_t header_len = dot1 - token;
|
||||
char *header = (char *)malloc(header_len + 1);
|
||||
strncpy(header, token, header_len);
|
||||
header[header_len] = '\0';
|
||||
|
||||
int header_bin_len;
|
||||
unsigned char *header_bin =
|
||||
jwt_base64url_decode(header, header_len, &header_bin_len);
|
||||
char *header_str = (char *)malloc(header_bin_len + 1);
|
||||
memcpy(header_str, header_bin, header_bin_len);
|
||||
header_str[header_bin_len] = '\0';
|
||||
|
||||
cJSON *json_header = cJSON_Parse(header_str);
|
||||
|
||||
free(header);
|
||||
free(header_bin);
|
||||
free(header_str);
|
||||
|
||||
return json_header;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef JWT_H
|
||||
#define JWT_H
|
||||
|
||||
#include <cJSON.h>
|
||||
|
||||
#define JWT_DEFAULT_SECRET "secret"
|
||||
|
||||
// Base64URL кодирование/декодирование
|
||||
char *jwt_base64url_encode(const unsigned char *input, int length);
|
||||
unsigned char *jwt_base64url_decode(const char *input, int input_len,
|
||||
int *output_len);
|
||||
|
||||
// Создание и верификация JWT
|
||||
char *jwt_createJWT(const char *header_json, const char *payload_json,
|
||||
const char *secret);
|
||||
unsigned char *jwt_hmac_sha256(const char *key, int key_len, const char *data,
|
||||
int data_len, unsigned int *digest_len);
|
||||
bool jwt_verifyJWT(const char *token, const char *secret);
|
||||
|
||||
// Получение данных из токена
|
||||
cJSON *jwt_get_payload(const char *token);
|
||||
cJSON *jwt_get_header(const char *token);
|
||||
|
||||
#endif // JWT_H
|
||||
+3191
File diff suppressed because it is too large.
Load diff
+306
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
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 18
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* 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
|
||||
|
||||
/* Limits the length of circular references can be before cJSON rejects to parse them.
|
||||
* This is to prevent stack overflows. */
|
||||
#ifndef CJSON_CIRCULAR_LIMIT
|
||||
#define CJSON_CIRCULAR_LIMIT 10000
|
||||
#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);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
|
||||
/* 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);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, 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 *item);
|
||||
|
||||
/* 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 item type and return its value */
|
||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
|
||||
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const 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/array 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.
|
||||
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
|
||||
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 *const *strings, int count);
|
||||
|
||||
/* Append item to the specified array/object. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(cJSON_bool) 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(cJSON_bool) 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(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
|
||||
|
||||
/* Remove/Detach 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(cJSON_bool) 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(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
CJSON_PUBLIC(cJSON_bool) 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);
|
||||
|
||||
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
|
||||
* The input pointer json cannot point to a read-only address area, such as a string constant,
|
||||
* but should point to a readable and writable address area. */
|
||||
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))
|
||||
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
|
||||
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
|
||||
|
||||
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
|
||||
#define cJSON_SetBoolValue(object, boolValue) ( \
|
||||
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
|
||||
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
|
||||
cJSON_Invalid\
|
||||
)
|
||||
|
||||
/* 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
|
||||
+25396
File diff suppressed because it is too large.
Load diff
+3780
File diff suppressed because it is too large.
Load diff
+44
@@ -0,0 +1,44 @@
|
||||
#include "user.h"
|
||||
#include <mongoose.h>
|
||||
#include "route.h"
|
||||
|
||||
static void root_handler(struct mg_connection *c, struct mg_http_message *hm) {
|
||||
struct mg_http_serve_opts opts = {.root_dir = "./web/"};
|
||||
mg_http_serve_dir(c, hm, &opts);
|
||||
}
|
||||
|
||||
static const struct route routes[] = {
|
||||
{"GET", "/", root_handler},
|
||||
{"POST", "/api/token", get_token},
|
||||
{"POST", "/api/token/verify", verify_token},
|
||||
{"POST", "/api/users", get_all_users},
|
||||
{nullptr, nullptr, nullptr}
|
||||
};
|
||||
|
||||
void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
const auto hm = (struct mg_http_message *)ev_data;
|
||||
bool route_found = false;
|
||||
|
||||
for (int i = 0; routes[i].path != nullptr; i++) {
|
||||
if (mg_match(hm->uri, mg_str(routes[i].path), nullptr) && mg_match(hm->method, mg_str(routes[i].method), nullptr)) {
|
||||
routes[i].handler(c, hm);
|
||||
route_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!route_found) {
|
||||
mg_http_reply(c, 404, "", "{%m:%m}", MG_ESC("error"), MG_ESC("URI not found"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr;
|
||||
mg_mgr_init(&mgr);
|
||||
mg_http_listen(&mgr, "http://0.0.0.0:8222", ev_handler, nullptr);
|
||||
while (true) {
|
||||
mg_mgr_poll(&mgr, 1000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
typedef void (*route_handler_t)(struct mg_connection *c, struct mg_http_message *hm);
|
||||
|
||||
struct route {
|
||||
char *method;
|
||||
char *path;
|
||||
route_handler_t handler;
|
||||
};
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
#include "user.h"
|
||||
#include "jwt.h"
|
||||
#include "utils.h"
|
||||
#include <cJSON.h>
|
||||
#include <mongoose.h>
|
||||
|
||||
#define JWT_SECRET_KEY "secret"
|
||||
|
||||
void get_all_users(struct mg_connection *c, struct mg_http_message *hm) {
|
||||
request_with_token *data = check_body(hm);
|
||||
if (data->error != NULL) {
|
||||
mg_http_reply(c, 403, "", "{%m:%m}", MG_ESC("error"), MG_ESC(data->error));
|
||||
return;
|
||||
}
|
||||
free_request_with_token(data);
|
||||
|
||||
User users[2] = {{.id = 1,
|
||||
.username = "tola",
|
||||
.email = "mail@example.com",
|
||||
.password = "passsword",
|
||||
.group = "root",
|
||||
.is_superuser = 1},
|
||||
{.id = 2,
|
||||
.username = "lisa",
|
||||
.email = "mail@example.com",
|
||||
.password = "asdf",
|
||||
.group = "users",
|
||||
.is_superuser = 0}};
|
||||
char msg[2048] = {0};
|
||||
strncpy(msg, "[", sizeof(msg));
|
||||
for (int i = 0; i < sizeof(users) / sizeof(User); i++) {
|
||||
snprintf(msg + strlen(msg), sizeof(msg) - strlen(msg),
|
||||
"{\"%s\":%d,\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%"
|
||||
"s\",\"%s\":%d}",
|
||||
"id", users[i].id, "username", users[i].username, "email",
|
||||
users[i].email, "password", users[i].password, "group",
|
||||
users[i].group, "is_superuser", users[i].is_superuser);
|
||||
if (i < sizeof(users) / sizeof(User) - 1) {
|
||||
strncat(msg, ",", strlen(msg));
|
||||
}
|
||||
}
|
||||
strncat(msg, "]\n", strlen(msg));
|
||||
mg_http_reply(c, 200, "Content-type: application/json\r\n", msg);
|
||||
}
|
||||
|
||||
void get_token(struct mg_connection *c, struct mg_http_message *hm) {
|
||||
if (hm->body.len == 0) {
|
||||
mg_http_reply(c, 400, "", "{%m:%m}", MG_ESC("error"),
|
||||
MG_ESC("must have username and password"));
|
||||
return;
|
||||
}
|
||||
cJSON *jsonbody = cJSON_Parse(hm->body.buf);
|
||||
cJSON *username = cJSON_GetObjectItem(jsonbody, "username");
|
||||
cJSON *password = cJSON_GetObjectItem(jsonbody, "password");
|
||||
if (username == NULL || password == NULL) {
|
||||
const char *error_ptr = cJSON_GetErrorPtr();
|
||||
if (error_ptr != NULL) {
|
||||
mg_http_reply(c, 400, "", "{%m:%m}", MG_ESC("error"), MG_ESC(error_ptr));
|
||||
return;
|
||||
}
|
||||
mg_http_reply(c, 400, "", "{%m:%m}", MG_ESC("error"),
|
||||
MG_ESC("must have username and password"));
|
||||
return;
|
||||
}
|
||||
const char *header = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}";
|
||||
const char *payload = "{\"sub\": \"1234567890\",\"username\": "
|
||||
"\"tola\",\"is_superuser\": true,\"iat\": 1516239022}";
|
||||
char *token = jwt_createJWT(header, payload, JWT_SECRET_KEY);
|
||||
mg_http_reply(c, 200, "", "{%m:%m}", MG_ESC("token"), MG_ESC(token));
|
||||
}
|
||||
|
||||
void verify_token(struct mg_connection *c, struct mg_http_message *hm) {
|
||||
const request_with_token *data = check_body(hm);
|
||||
if (data->error != nullptr) {
|
||||
mg_http_reply(c, 403, "", "{%m:%m}", MG_ESC("error"), MG_ESC(data->error));
|
||||
return;
|
||||
}
|
||||
const bool valid = jwt_verifyJWT(data->token, JWT_SECRET_KEY);
|
||||
if (!valid) {
|
||||
mg_http_reply(c, 403, "", "{%m:%m}", MG_ESC("error"),
|
||||
MG_ESC("invalid token"));
|
||||
return;
|
||||
}
|
||||
mg_http_reply(c, 200, "", "{%m:%m}", MG_ESC("valid"), MG_ESC("true"));
|
||||
}
|
||||
|
||||
request_with_token *new_request_with_token(char *token, cJSON *body,
|
||||
char *error) {
|
||||
request_with_token *data = malloc(sizeof(request_with_token));
|
||||
data->token = token;
|
||||
data->body = body;
|
||||
data->error = error;
|
||||
return data;
|
||||
}
|
||||
|
||||
void free_request_with_token(request_with_token *data) {
|
||||
if (data->body != nullptr) {
|
||||
cJSON_Delete(data->body);
|
||||
}
|
||||
if (data->token != nullptr) {
|
||||
free(data->token);
|
||||
}
|
||||
if (data->error != nullptr) {
|
||||
free(data->error);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cJSON.h>
|
||||
#include <mongoose.h>
|
||||
|
||||
typedef struct User {
|
||||
int id;
|
||||
char username[256];
|
||||
char email[256];
|
||||
char password[256];
|
||||
char group[256];
|
||||
int is_superuser;
|
||||
} User;
|
||||
|
||||
typedef struct request_with_token {
|
||||
char *token;
|
||||
cJSON *body;
|
||||
char *error;
|
||||
} request_with_token;
|
||||
|
||||
typedef struct get_token_request {
|
||||
char *username;
|
||||
char *password;
|
||||
char *error;
|
||||
} get_token_request;
|
||||
|
||||
request_with_token *new_request_with_token(char *token, cJSON *body,
|
||||
char *error);
|
||||
void free_request_with_token(request_with_token *data);
|
||||
|
||||
void get_all_users(struct mg_connection *c, struct mg_http_message *hm);
|
||||
void get_token(struct mg_connection *c, struct mg_http_message *hm);
|
||||
void verify_token(struct mg_connection *c, struct mg_http_message *hm);
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#include "utils.h"
|
||||
#include "user.h"
|
||||
#include <cJSON.h>
|
||||
#include <mongoose.h>
|
||||
|
||||
request_with_token *check_body(const struct mg_http_message *hm) {
|
||||
if (hm->body.len == 0) {
|
||||
return new_request_with_token(nullptr, nullptr, (char *)"must have token");
|
||||
}
|
||||
|
||||
cJSON *json_body = cJSON_Parse(hm->body.buf);
|
||||
const cJSON *token = cJSON_GetObjectItem(json_body, "token");
|
||||
if (token == NULL) {
|
||||
const char *error_ptr = cJSON_GetErrorPtr();
|
||||
if (error_ptr != NULL) {
|
||||
return new_request_with_token(nullptr, nullptr, (char *)error_ptr);
|
||||
}
|
||||
return new_request_with_token(nullptr, nullptr, (char *)"must have token");
|
||||
}
|
||||
return new_request_with_token(strdup(token->valuestring), json_body, nullptr);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "user.h"
|
||||
|
||||
request_with_token *check_body(const struct mg_http_message *hm);
|
||||
@@ -0,0 +1,11 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>fmongoose</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>it's work</h1>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user