Compare commits
10
Commits
dc52c20098
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6347444494 | ||
|
|
f43bb30065 | ||
|
|
e00ffe6f70 | ||
|
|
b9e247b88f | ||
|
|
45b4f6bee0 | ||
|
|
c9c9bbbff6 | ||
|
|
cbdf7b2523 | ||
|
|
08e771eaa0 | ||
|
|
e623c0dce1 | ||
|
|
e3e84a0441 |
-101
@@ -1,101 +0,0 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file log.c
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief cStuff log module source file
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
#include <time.h>
|
||||
#include "log.h"
|
||||
|
||||
|
||||
void
|
||||
cstuff_log_init(cstuff_log_t self, uint8_t level)
|
||||
{
|
||||
self->target = stderr;
|
||||
self->level = level;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
cstuff_log_release(cstuff_log_t self)
|
||||
{
|
||||
if (self->target != stderr)
|
||||
fclose(self->target);
|
||||
}
|
||||
|
||||
|
||||
cstuff_retcode_t
|
||||
cstuff_log_set_file(cstuff_log_t self, const char * filename)
|
||||
{
|
||||
FILE * fp;
|
||||
|
||||
|
||||
if ( !(fp = fopen(filename, "a")) )
|
||||
return CSTUFF_SYSCALL_ERROR;
|
||||
|
||||
self->target = fp;
|
||||
return CSTUFF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
cstuff_log_printf(cstuff_log_t self, uint8_t level, const char * format, ...)
|
||||
{
|
||||
int result;
|
||||
va_list vl;
|
||||
|
||||
va_start(vl, format);
|
||||
result = cstuff_log_vprintf(self, level, format, vl);
|
||||
va_end(vl);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
cstuff_log_vprintf(cstuff_log_t self,
|
||||
uint8_t level,
|
||||
const char * format,
|
||||
va_list vl)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (self->level & level)
|
||||
{
|
||||
FILE * f = self->target;
|
||||
char * label;
|
||||
time_t rawtime;
|
||||
char strtime[32+1];
|
||||
struct tm timeinfo;
|
||||
|
||||
time (&rawtime);
|
||||
localtime_r (&rawtime, &timeinfo);
|
||||
strftime (strtime, 32, "%F %T", &timeinfo);
|
||||
|
||||
switch (level)
|
||||
{
|
||||
case CSTUFF_LOG_ERROR : label = "!!"; break;
|
||||
case CSTUFF_LOG_ALERT : label = "~!"; break;
|
||||
case CSTUFF_LOG_DEBUG : label = "**"; break;
|
||||
default : label = "--";
|
||||
}
|
||||
|
||||
result = fprintf(f, "%s %s ", strtime, label);
|
||||
result += vfprintf(f, format, vl);
|
||||
fputc('\n', f);
|
||||
result++;
|
||||
fflush(f);
|
||||
}
|
||||
else
|
||||
result = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file log.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief cStuff log module header file
|
||||
*
|
||||
* @see https://lowenware.com/cStuff/
|
||||
*/
|
||||
|
||||
#ifndef CSTUFF_LOG_H_557ADEFE_2BE8_4B9B_A894_F08FF64008EF
|
||||
#define CSTUFF_LOG_H_557ADEFE_2BE8_4B9B_A894_F08FF64008EF
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include "retcode.h"
|
||||
|
||||
#define CSTUFF_LOG_OFF (0)
|
||||
#define CSTUFF_LOG_ERROR (1<<0)
|
||||
#define CSTUFF_LOG_ALERT (1<<1)
|
||||
#define CSTUFF_LOG_STATE (1<<2)
|
||||
#define CSTUFF_LOG_DEBUG (1<<3)
|
||||
#define CSTUFF_LOG_ALL (0xFF)
|
||||
|
||||
struct cstuff_log
|
||||
{
|
||||
FILE * target;
|
||||
uint8_t level;
|
||||
};
|
||||
|
||||
typedef struct cstuff_log * cstuff_log_t;
|
||||
|
||||
|
||||
void
|
||||
cstuff_log_init(cstuff_log_t self, uint8_t level);
|
||||
|
||||
|
||||
void
|
||||
cstuff_log_release(cstuff_log_t self);
|
||||
|
||||
|
||||
cstuff_retcode_t
|
||||
cstuff_log_set_file(cstuff_log_t self, const char * filename);
|
||||
|
||||
|
||||
int
|
||||
cstuff_log_printf(cstuff_log_t self, uint8_t level, const char * format, ...);
|
||||
|
||||
|
||||
int
|
||||
cstuff_log_vprintf( cstuff_log_t self,
|
||||
uint8_t level,
|
||||
const char * format,
|
||||
va_list vl );
|
||||
|
||||
#endif /* !CSTUFF_LOG_H */
|
||||
@@ -1,28 +0,0 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file retcode.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief cStuff return codes
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#ifndef CSTUFF_RETCODE_H_CBA3C16B_88D6_49F4_966D_DBDA727583FE
|
||||
#define CSTUFF_RETCODE_H_CBA3C16B_88D6_49F4_966D_DBDA727583FE
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CSTUFF_MALLOC_ERROR = -2
|
||||
, CSTUFF_SYSCALL_ERROR = -1
|
||||
, CSTUFF_SUCCESS = 0
|
||||
, CSTUFF_IDLE = 1
|
||||
|
||||
} cstuff_retcode_t;
|
||||
|
||||
#endif /* !CSTUFF_RETCODE_H */
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file string.c
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief cStuff string module implementation
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "string.h"
|
||||
|
||||
int
|
||||
cstuff_strcpy(char **out, const char *in)
|
||||
{
|
||||
return cstuff_strncpy(out, in, strlen(in));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
cstuff_strncpy(char **out, const char *in, int len)
|
||||
{
|
||||
char *s;
|
||||
|
||||
if (!(s = malloc( len + 1 )))
|
||||
return -1;
|
||||
|
||||
strncpy(s, in, len);
|
||||
s[len]=0;
|
||||
|
||||
*out = s;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
cstuff_strset(char **out, const char *in)
|
||||
{
|
||||
return cstuff_strnset(out, in, strlen(in));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
cstuff_strnset(char **out, const char *in, int len)
|
||||
{
|
||||
char *s;
|
||||
|
||||
if (!(s = realloc(*out, len+1)))
|
||||
return -1;
|
||||
|
||||
strncpy(s, in, len);
|
||||
s[len] = 0;
|
||||
|
||||
*out = s;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
cstuff_sprintf(char **out, const char *format, ...)
|
||||
{
|
||||
int result;
|
||||
va_list vl;
|
||||
|
||||
va_start(vl, format);
|
||||
result = cstuff_vsprintf(out, format, vl);
|
||||
va_end(vl);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
cstuff_vsprintf(char **out, const char *format, va_list args)
|
||||
{
|
||||
int result;
|
||||
va_list vc;
|
||||
char tmp, *s;
|
||||
|
||||
va_copy(vc, args);
|
||||
result = vsnprintf(&tmp, 1, format, vc);
|
||||
va_end(vc);
|
||||
|
||||
if (!(s = malloc(result + 1)))
|
||||
return -1;
|
||||
|
||||
*out = s;
|
||||
result = vsnprintf(s, result+1, format, args);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file string.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief String module declarations
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#ifndef CSTUFF_STRING_H_938B242C_B750_40E9_8B67_A69F2F37EB87
|
||||
#define CSTUFF_STRING_H_938B242C_B750_40E9_8B67_A69F2F37EB87
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "retcode.h"
|
||||
|
||||
|
||||
/** @brief Copies in string to newly allocated out buffer
|
||||
* @param out a pointer to an address where new pointer must be stored
|
||||
* @param in an input string
|
||||
* @return length of string or -1 if out of memory
|
||||
*/
|
||||
int
|
||||
cstuff_strcpy(char **out, const char *in);
|
||||
|
||||
|
||||
int
|
||||
cstuff_strncpy(char **out, const char *in, int len);
|
||||
|
||||
|
||||
/** @brief Overwrites content of out buffer with in string using realloc
|
||||
* @param out a pointer to an address where new string must be stored
|
||||
* @param in an input string
|
||||
* @return length of string or -1 if out of memory
|
||||
*/
|
||||
int
|
||||
cstuff_strset(char **out, const char *in);
|
||||
|
||||
|
||||
int
|
||||
cstuff_strnset(char **out, const char *in, int len);
|
||||
|
||||
|
||||
/** @brief Allocates memory and prints a formatted string into it
|
||||
* @param out a pointer to an address where new string must be stored
|
||||
* @param format a format for string (see manual for stdlib sprintf)
|
||||
* @return length of string or -1 if out of memory
|
||||
*/
|
||||
int
|
||||
cstuff_sprintf(char **out, const char *format, ...);
|
||||
|
||||
|
||||
int
|
||||
cstuff_vsprintf(char **out, const char *format, va_list args);
|
||||
|
||||
#endif /* !CSTUFF_STRING_H */
|
||||
@@ -0,0 +1,265 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file crypt.c
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#include <openssl/rand.h>
|
||||
#include "crypt.h"
|
||||
|
||||
|
||||
static AislStatus
|
||||
ax_crypt__read_key_file(const char *key_file, unsigned char *key,
|
||||
unsigned char *salt)
|
||||
{
|
||||
AislStatus result = AISL_SYSCALL_ERROR;
|
||||
int i;
|
||||
FILE *f;
|
||||
char buff[2*(AX_CRYPT_KEY_SIZE + AX_CRYPT_SALT_SIZE) + 1], char hex[2 + 1],
|
||||
*ptr = buff, *ep = NULL;
|
||||
|
||||
if (!(f = fopen(key_file))) {
|
||||
goto finally;
|
||||
}
|
||||
|
||||
if (fread(buff, 1, sizeof (buff), f) != sizeof (buff)) {
|
||||
goto e_input;
|
||||
}
|
||||
|
||||
hex[2] = 0;
|
||||
|
||||
for (i = 0; i < AX_CRYPT_SALT_SIZE; i++) {
|
||||
memcpy(hex, ptr, 2);
|
||||
salt[i] = strtol(hex, &ep, 16);
|
||||
if (ep != &hex[2]) {
|
||||
goto e_input;
|
||||
}
|
||||
ptr += 2;
|
||||
}
|
||||
|
||||
if (*(ptr++) != '$') {
|
||||
goto e_input;
|
||||
}
|
||||
|
||||
for (i = 0; i < AX_CRYPT_KEY_SIZE; i++) {
|
||||
memcpy(hex, ptr, 2);
|
||||
key[i] = strtol(hex, &ep, 16);
|
||||
if (ep != &hex[2]) {
|
||||
goto e_input;
|
||||
}
|
||||
ptr += 2;
|
||||
}
|
||||
|
||||
result = AISL_SUCCESS;
|
||||
goto cleanup;
|
||||
|
||||
e_input:
|
||||
result = AISL_INPUT_ERROR;
|
||||
|
||||
cleanup:
|
||||
fclose(f);
|
||||
|
||||
finally:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
ax_crypt__write_key_file(const char *key_file, unsigned char *key,
|
||||
unsigned char *salt)
|
||||
{
|
||||
AislStatus result = AISL_SYSCALL_ERROR;
|
||||
int i;
|
||||
FILE *f;
|
||||
|
||||
if (!(f = fopen(key_file))) {
|
||||
goto finally;
|
||||
}
|
||||
|
||||
for (i = 0; i < AX_CRYPT_SALT_SIZE; i++) {
|
||||
if (fprintf(f, "%02x", salt[i] & 0xFF) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (fputc('$', f) == EOF) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i < AX_CRYPT_KEY_SIZE; i++) {
|
||||
if (fprintf(f, "%02x", key[i] & 0xFF) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
result = AISL_SUCCESS;
|
||||
goto cleanup;
|
||||
|
||||
cleanup:
|
||||
fclose(f);
|
||||
chmod(key_file, S_IRUSR);
|
||||
|
||||
finally:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_crypt_init(AxSession crypt, const char *key_file)
|
||||
{
|
||||
AislStatus result;
|
||||
unsigned char key[AX_CRYPT_KEY_SIZE], salt[AX_CRYPT_SALT_SIZE],
|
||||
evp_key[AX_CRYPT_KEY_SIZE], iv[AX_CRYPT_KEY_SIZE];
|
||||
|
||||
if (cstuff_file_exists(key_file)) {
|
||||
result = ax_crypt__read_key_file(key_file, key, salt);
|
||||
} else {
|
||||
RAND_bytes(key, sizeof (key));
|
||||
RAND_bytes(salt, sizeof (salt));
|
||||
result = ax_crypt__write_key_file(key_file, key, salt);
|
||||
}
|
||||
|
||||
if (result != AISL_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key, sizeof (key), 5,
|
||||
key, iv)) {
|
||||
return AISL_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
if ((crypt->e_ctx = EVP_CIPHER_CTX_new())) {
|
||||
EVP_CIPHER_CTX_init(crypt->e_ctx);
|
||||
EVP_EncryptInit_ex(crypt->e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
|
||||
if ((crypt->d_ctx = EVP_CIPHER_CTX_new())) {
|
||||
EVP_CIPHER_CTX_init(crypt->d_ctx);
|
||||
EVP_DecryptInit_ex(crypt->d_ctx, EVP_aes_256_cbc(), NULL, key, iv);
|
||||
return result;
|
||||
} else {
|
||||
EVP_CIPHER_CTX_free(crypt->e_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ax_crypt_release(AxSession crypt)
|
||||
{
|
||||
if (crypt->e_ctx) {
|
||||
EVP_CIPHER_CTX_free(crypt->e_ctx);
|
||||
}
|
||||
|
||||
if (crypt->d_ctx) {
|
||||
EVP_CIPHER_CTX_free(crypt->d_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ax_crypt_write(AxSession crypt, char **p_out, const char *input, int in_len)
|
||||
{
|
||||
int result = in_len, l;
|
||||
unsigned char *out;
|
||||
EVP_CIPHER_CTX *e_ctx = crypt->e_ctx;
|
||||
|
||||
if (!(out = calloc(result + EVP_MAX_BLOCK_LENGTH, sizeof (char)))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
EVP_EncryptInit_ex(e_ctx, NULL, NULL, NULL, NULL);
|
||||
EVP_EncryptUpdate(e_ctx, out, &result, (const unsigned char *)input, in_len);
|
||||
EVP_EncryptFinal_ex(e_ctx, &out[result], &l);
|
||||
*p_out = (char *)out;
|
||||
return result + l;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ax_crypt_read(AxSession crypt, char **p_out, const char *input, int in_len)
|
||||
{
|
||||
int result = in_len, l;
|
||||
unsigned char *out;
|
||||
EVP_CIPHER_CTX *d_ctx = crypt->d_ctx;
|
||||
|
||||
if (!(out = malloc(in_len + EVP_MAX_BLOCK_LENGTH))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
EVP_DecryptInit_ex(d_ctx, NULL, NULL, NULL, NULL);
|
||||
EVP_DecryptUpdate(d_ctx, out, &result, input, in_len);
|
||||
EVP_DecryptFinal_ex(d_ctx, &out[result], &l);
|
||||
|
||||
*p_out = (char *)out;
|
||||
return result + l;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ax_crypt_encode(AxSession crypt, char **p_out, const char *input, int in_len)
|
||||
{
|
||||
int result = -1;
|
||||
BIO *bio, *b64;
|
||||
BUF_MEM *bptr;
|
||||
unsigned char *out, *cipher;
|
||||
|
||||
if ((in_len = ax_crypt_write(crypt, &cipher, input, in_len)) != -1) {
|
||||
if ((b64 = BIO_new( BIO_f_base64()))) {
|
||||
if ((bio = BIO_new( BIO_s_mem()))) {
|
||||
if ((b64 = BIO_push(b64, bio))) {
|
||||
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
||||
BIO_write(b64, cipher, in_len);
|
||||
BIO_flush(b64);
|
||||
BIO_get_mem_ptr(b64, &bptr);
|
||||
result = bptr->length;
|
||||
if ((out = malloc(result + 1))) {
|
||||
memcpy(out, bptr->data, result);
|
||||
out[result] = '\0';
|
||||
*p_out = (char *)out;
|
||||
} else {
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
BIO_free_all(b64);
|
||||
}
|
||||
free(cipher);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ax_crypt_decode(AxSession crypt, char **p_out, const char *input, int in_len)
|
||||
{
|
||||
int result = -1;
|
||||
BIO *bio, *b64;
|
||||
BUF_MEM *bptr;
|
||||
unsigned char *out, *cipher;
|
||||
|
||||
if ((cipher = malloc(in_len * sizeof (unsigned char)))) {
|
||||
if ((bio = BIO_new_mem_buf((void*)input, in_len))) {
|
||||
if ((b64 = BIO_new(BIO_f_base64()))) {
|
||||
if ((bio = BIO_push(b64, bio))) {
|
||||
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
|
||||
in_len = BIO_read(bio, cipher, in_len);
|
||||
result = ax_crypt_read(crypt, p_out, cipher, in_len);
|
||||
}
|
||||
}
|
||||
BIO_free_all(bio);
|
||||
}
|
||||
free(cipher);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file crypt.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#ifndef AX_CRYPT_H_F18E0303_66D9_4793_A902_230BA0A0A46F
|
||||
#define AX_CRYPT_H_F18E0303_66D9_4793_A902_230BA0A0A46F
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <aisl/aisl.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#define AX_CRYPT_KEY_SIZE 32
|
||||
#define AX_CRYPT_SALT_SIZE 8
|
||||
|
||||
struct ax_crypt {
|
||||
EVP_CIPHER_CTX *e_ctx;
|
||||
EVP_CIPHER_CTX *d_ctx;
|
||||
};
|
||||
|
||||
|
||||
typedef struct ax_crypt * AxCrypt;
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_crypt_init(AxSession crypt, const char *key_file);
|
||||
|
||||
|
||||
void
|
||||
ax_crypt_release(AxSession crypt);
|
||||
|
||||
|
||||
/* encrypt */
|
||||
int
|
||||
ax_crypt_write(AxSession crypt, char **out, const char *input, int in_len);
|
||||
|
||||
|
||||
/* decrypt */
|
||||
int
|
||||
ax_crypt_read(AxSession crypt, char **out, const char *input, int in_len);
|
||||
|
||||
|
||||
/* encrypt and convert to BASE64 */
|
||||
int
|
||||
ax_crypt_encode(AxSession crypt, char **out, const char *input, int in_len);
|
||||
|
||||
|
||||
/* decrypt from BASE64 */
|
||||
int
|
||||
ax_crypt_decode(AxSession crypt, char **out, const char *input, int in_len);
|
||||
|
||||
#endif /* !AX_CRYPT_H */
|
||||
@@ -0,0 +1,465 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file html.c
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <cStuff/string.h>
|
||||
|
||||
#include "html.h"
|
||||
|
||||
|
||||
static char *
|
||||
ax_html_escape(char *src, uint32_t len)
|
||||
{
|
||||
uint32_t extra = 0;
|
||||
char *ptr = src;
|
||||
char ch, *result;
|
||||
|
||||
while ((ch = *ptr) != 0) {
|
||||
switch (ch) {
|
||||
case '"':
|
||||
case '\\':
|
||||
case '\n':
|
||||
extra++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if ((result = calloc(len + extra + 1, sizeof (char))) != NULL) {
|
||||
if (extra) {
|
||||
ptr = result;
|
||||
while (len--) {
|
||||
switch (*src) {
|
||||
case '"':
|
||||
case '\\':
|
||||
*(ptr++) = '\\';
|
||||
break;
|
||||
case '\n':
|
||||
*(ptr++) = '\\';
|
||||
*(ptr++) = 'n';
|
||||
case '\r': /* go through */
|
||||
src++;
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!(*(ptr++) = *(src++)))
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
strncpy(result, src, len);
|
||||
result[len] = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct ax_html *
|
||||
ax_html_append_new(CStuffList list, const struct ax_html *src)
|
||||
{
|
||||
struct ax_html *result;
|
||||
|
||||
if ((result = malloc(sizeof (*result))) != NULL) {
|
||||
result->type = src->type;
|
||||
result->offset = src->offset;
|
||||
result->size = src->size;
|
||||
if ((result->data = ax_html_escape(src->data, src->size)) != NULL) {
|
||||
if (cstuff_list_append(list, result) != -1) {
|
||||
return result;
|
||||
}
|
||||
free(result->data);
|
||||
}
|
||||
free(result);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ax_html_check_chars(char *begin, char *end)
|
||||
{
|
||||
int result = 0;
|
||||
while (begin < end) {
|
||||
char c = *begin;
|
||||
if (c == '-') {
|
||||
c = (*begin = '_');
|
||||
}
|
||||
if (isalnum(c) || c == '_') {
|
||||
result++;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
begin++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
ax_html_read_line(CStuffList list, char *line)
|
||||
{
|
||||
const char input_open[] = "${",
|
||||
input_close[] = "}",
|
||||
block_open[] = "<!--@",
|
||||
block_close[] = "-->";
|
||||
|
||||
struct ax_html node;
|
||||
size_t offset;
|
||||
char *cur;
|
||||
|
||||
offset = strspn(line, "\t ");
|
||||
cur = &line[offset];
|
||||
|
||||
while (*cur) {
|
||||
char *p1, *p2, *p;
|
||||
uint16_t type = AX_HTML_PLAIN;
|
||||
uint32_t close_len = 0;
|
||||
|
||||
p1 = strstr(cur, input_open);
|
||||
p2 = strstr(cur, block_open);
|
||||
|
||||
if (p1 && (!p2 || p2 > p1)) {
|
||||
p = &p1[sizeof (input_open) - 1];
|
||||
if ((p2 = strstr(p, input_close)) && ax_html_check_chars(p, p2)) {
|
||||
type = AX_HTML_INPUT;
|
||||
close_len = sizeof (input_close) - 1;
|
||||
}
|
||||
} else if (p2 && (!p1 || p1 > p2)) {
|
||||
p1 = p2;
|
||||
p = &p1[sizeof (block_open) - 1];
|
||||
if ((p2 = strstr(p, block_close)) && ax_html_check_chars(p, p2)) {
|
||||
type = AX_HTML_BLOCK;
|
||||
close_len = sizeof (block_close) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (type != AX_HTML_PLAIN) {
|
||||
if (p1 != cur) {
|
||||
node.type = AX_HTML_PLAIN;
|
||||
node.offset = offset;
|
||||
node.size = (uint32_t)(p1 - cur);
|
||||
node.data = cur;
|
||||
offset = 0;
|
||||
if (ax_html_append_new(list, &node) == NULL) {
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
}
|
||||
node.type = type;
|
||||
node.offset = offset;
|
||||
node.size = (uint32_t)(p2 - p);
|
||||
node.data = p;
|
||||
offset = 0;
|
||||
if (ax_html_append_new(list, &node) == NULL) {
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
cur = p2 + close_len;
|
||||
if (type == AX_HTML_BLOCK) {
|
||||
size_t lfcr = strspn(cur, "\r\n");
|
||||
if (lfcr) {
|
||||
cur += lfcr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node.type = type;
|
||||
node.offset = offset;
|
||||
node.size = strlen(cur);
|
||||
node.data = cur;
|
||||
if (ax_html_append_new(list, &node) == NULL) {
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
cur = &cur[node.size];
|
||||
}
|
||||
}
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_html_import(CStuffList list, const char *html_file)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
size_t sz = 256;
|
||||
char *line;
|
||||
ssize_t rs;
|
||||
AislStatus result = AISL_SUCCESS;
|
||||
|
||||
if (!(line = malloc(sz)))
|
||||
goto e_malloc;
|
||||
|
||||
if (!(f = fopen(html_file, "r")))
|
||||
goto e_syscall;
|
||||
|
||||
while ((rs = getdelim(&line, &sz, '\n', f)) != -1) {
|
||||
if ((result = ax_html_read_line(list, line)) != AISL_SUCCESS) {
|
||||
goto finally;
|
||||
}
|
||||
}
|
||||
|
||||
switch(errno) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case ENOMEM:
|
||||
goto e_malloc;
|
||||
|
||||
default:
|
||||
goto e_syscall;
|
||||
}
|
||||
goto finally;
|
||||
|
||||
e_malloc:
|
||||
result = AISL_MALLOC_ERROR;
|
||||
goto finally;
|
||||
|
||||
e_syscall:
|
||||
result = AISL_SYSCALL_ERROR;
|
||||
goto finally;
|
||||
|
||||
finally:
|
||||
if (line)
|
||||
free(line);
|
||||
if (f)
|
||||
fclose(f);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_html_export(CStuffList list, const struct ax_html_options *opts)
|
||||
{
|
||||
AislStatus result = AISL_SUCCESS;
|
||||
FILE *out = NULL;
|
||||
int i, l;
|
||||
char *c_prefix = NULL, *u_prefix = NULL, *f = NULL;
|
||||
const char *ofst = opts->offset;
|
||||
|
||||
/* Cammel case prefix */
|
||||
if (cstuff_strcpy(&c_prefix, opts->name) == -1)
|
||||
goto e_malloc;
|
||||
|
||||
*c_prefix = toupper(*c_prefix);
|
||||
|
||||
/* Upper case prefix */
|
||||
if ((i = cstuff_strcpy(&u_prefix, opts->name)) == -1)
|
||||
goto e_malloc;
|
||||
|
||||
while (i--) {
|
||||
u_prefix[i] = toupper(u_prefix[i]);
|
||||
}
|
||||
|
||||
/* header file */
|
||||
if ((l = cstuff_sprintf(&f, "%s/%s.h", opts->path, opts->name)) == -1)
|
||||
goto e_malloc;
|
||||
|
||||
if (!(out = fopen(f, "w")))
|
||||
goto e_syscall;
|
||||
|
||||
fprintf(out, "#ifndef %s_H_AISL_COMPOSED\n", u_prefix);
|
||||
fprintf(out, "#define %s_H_AISL_COMPOSED\n\n", u_prefix);
|
||||
fprintf(out, "#include <aisl/aisl.h>\n");
|
||||
|
||||
fprintf(out, "#ifdef DEVEL\n\n");
|
||||
fprintf(out, "#include <cStuff/list.h>\n");
|
||||
fprintf(out, "#else\n\n");
|
||||
fprintf(out, "#define %s_put(STREAM, BLOCK, CB, CTX) ", opts->name);
|
||||
fprintf(out, "%s_put_##BLOCK##(STREAM, CB, CTX)\n\n", opts->name);
|
||||
fprintf(out, "#endif\n\n");
|
||||
fprintf(out, "typedef int\n(*%sCallback)(AislStream s, const char *input, "
|
||||
"void *ctx);\n\n", c_prefix);
|
||||
|
||||
fprintf(out, "#ifdef DEVEL\n\n");
|
||||
fprintf(out, "int\n%s_put(AislStream s, %sCallback callback, "
|
||||
"void *ctx, const char *block);\n\n", opts->name, c_prefix);
|
||||
fprintf(out, "int\n%s_set(CStuffList list);\n\n", opts->name);
|
||||
fprintf(out, "#else\n\n");
|
||||
for (i = 0; i < list->length; i++) {
|
||||
struct ax_html *html = list->items[i];
|
||||
|
||||
if (html->type == AX_HTML_BLOCK) {
|
||||
fprintf(out, "int\n%s_put_%s(AislStream s, %sCallback callback, void *ctx);\n\n",
|
||||
opts->name, html->data, c_prefix);
|
||||
}
|
||||
}
|
||||
fprintf(out, "#endif\n\n");
|
||||
|
||||
fprintf(out, "#endif\n");
|
||||
fprintf(out, "\n\n");
|
||||
|
||||
fclose(out);
|
||||
|
||||
/* source file */
|
||||
f[l-1] = 'c';
|
||||
|
||||
if (!(out = fopen(f, "w")))
|
||||
goto e_syscall;
|
||||
|
||||
fprintf(out, "#include <stdint.h>\n");
|
||||
fprintf(out, "#include \"%s.h\"\n\n", opts->name);
|
||||
fprintf(out, "#define %s_PLAIN %d\n", u_prefix, AX_HTML_PLAIN);
|
||||
fprintf(out, "#define %s_BLOCK %d\n", u_prefix, AX_HTML_BLOCK);
|
||||
fprintf(out, "#define %s_INPUT %d\n\n", u_prefix, AX_HTML_INPUT);
|
||||
fprintf(out, "struct %s {\n", opts->name);
|
||||
fprintf(out, "%suint16_t type;\n", ofst);
|
||||
fprintf(out, "%suint16_t offset;\n", ofst);
|
||||
fprintf(out, "%suint32_t size;\n", ofst);
|
||||
fprintf(out, "%sconst char *code;\n", ofst);
|
||||
fprintf(out, "};\n\n");
|
||||
|
||||
fprintf(out, "#ifdef DEVEL\n\n");
|
||||
|
||||
fprintf(out, "static struct %s *m_%s = NULL;\n\n", opts->name, opts->name);
|
||||
fprintf(out, "#else\n\n");
|
||||
|
||||
fprintf(out, "static const struct %s m_%s[] = {\n", opts->name, opts->name);
|
||||
|
||||
for (i = 0; i < list->length; i++) {
|
||||
struct ax_html *html = list->items[i];
|
||||
uint32_t offset, size = html->size;
|
||||
|
||||
if (opts->minimize) {
|
||||
offset = 0;
|
||||
if (html->type == AX_HTML_PLAIN) {
|
||||
int l = strlen(html->data);
|
||||
|
||||
if (!(l < 2) && !strcmp(&html->data[l - 2], "\\n")) {
|
||||
size--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
offset = html->offset & 0xFFFF;
|
||||
}
|
||||
|
||||
fprintf(out, "%s{%u, %u, %u, \"%s\"},\n", ofst, html->type, offset,
|
||||
size, html->data);
|
||||
}
|
||||
fprintf(out, "%s{0, 0, 0, NULL}\n};\n\n", ofst);
|
||||
|
||||
fprintf(out, "#endif\n\n");
|
||||
|
||||
fprintf(out, "static int\nput_from(AislStream s, %sCallback callback, "
|
||||
"void *ctx, uint32_t from)\n{\n", c_prefix);
|
||||
fprintf(out, "%sint result = 0, r;\n", ofst);
|
||||
fprintf(out, "%swhile (m_%s[from]->data) {\n", ofst, opts->name);
|
||||
fprintf(out, "%s%sswitch (m_%s[from]->type) {\n", ofst, ofst,
|
||||
opts->name);
|
||||
fprintf(out, "%s%scase %s_PLAIN:\n", ofst, ofst, u_prefix);
|
||||
fprintf(out, "%s%s%sr = aisl_write(s, m_%s[from]->data, m_%s[from]->size);\n",
|
||||
ofst, ofst, ofst, opts->name, opts->name);
|
||||
fprintf(out, "%s%s%sbreak;\n", ofst, ofst, ofst);
|
||||
|
||||
fprintf(out, "%s%scase %s_INPUT:\n", ofst, ofst, u_prefix);
|
||||
fprintf(out, "%s%s%sr = callback(s, m_%s[from]->data, ctx);\n",
|
||||
ofst, ofst, ofst, opts->name);
|
||||
fprintf(out, "%s%s%sbreak;\n", ofst, ofst, ofst);
|
||||
fprintf(out, "%s%scase %s_BLOCK:\n", ofst, ofst, u_prefix);
|
||||
fprintf(out, "%s%s%sreturn result;\n", ofst, ofst, ofst);
|
||||
fprintf(out, "%s%sdefault:\n", ofst, ofst);
|
||||
fprintf(out, "%s%s%sreturn -1;\n", ofst, ofst, ofst);
|
||||
fprintf(out, "%s%s}\n", ofst, ofst);
|
||||
fprintf(out, "%s%sif (r == -1) return r;\n", ofst, ofst);
|
||||
fprintf(out, "%s%sresult += r;\n", ofst, ofst);
|
||||
fprintf(out, "%s%sfrom++;\n", ofst, ofst);
|
||||
fprintf(out, "%s}\n", ofst);
|
||||
fprintf(out, "%sreturn result;\n}\n\n", ofst);
|
||||
|
||||
fprintf(out, "#ifdef DEVEL\n\n");
|
||||
fprintf(out, "int\n%s_put(AislStream s, %sCallback callback, "
|
||||
"void *ctx, const char *block)\n{\n", opts->name, c_prefix);
|
||||
fprintf(out, "%sint i = 0;\n", ofst);
|
||||
fprintf(out, "%swhile (m_%s[i]->data) {\n", ofst, opts->name);
|
||||
fprintf(out, "%s%sif (m_%s[i]->type == %s_BLOCK && "
|
||||
"!strcmp(m_%s[i]->data, block)) {\n", ofst, ofst, opts->name, u_prefix,
|
||||
opts->name);
|
||||
fprintf(out, "%s%s%sreturn put_from(s, callback, ctx, i + 1);\n", ofst, ofst,
|
||||
ofst);
|
||||
fprintf(out, "%s%s}\n", ofst, ofst);
|
||||
fprintf(out, "%s}\n", ofst);
|
||||
fprintf(out, "%sreturn 0;\n}\n\n", ofst);
|
||||
|
||||
fprintf(out, "int\n%s_set(CStuffList list)\n{\n", opts->name);
|
||||
fprintf(out, "%sint i;\n", ofst);
|
||||
fprintf(out, "%sif (m_%s) {\n", ofst, opts->name);
|
||||
fprintf(out, "%s%si = 0;\n", ofst, ofst);
|
||||
fprintf(out, "%s%swhile (m_%s[i].data) {\n", ofst, ofst, opts->name);
|
||||
fprintf(out, "%s%s%sfree(m_%s[i].data);\n", ofst, ofst, ofst, opts->name);
|
||||
fprintf(out, "%s%s}\n", ofst, ofst);
|
||||
fprintf(out, "%s%sfree(m_%s);\n", ofst, ofst, opts->name);
|
||||
fprintf(out, "%s}\n", ofst);
|
||||
fprintf(out, "%sif (!(m_%s = malloc(list->length * sizeof (*m_%s))))\n", ofst,
|
||||
opts->name, opts->name);
|
||||
fprintf(out, "%s%sreturn -1;\n", ofst, ofst);
|
||||
fprintf(out, "%sfor (i = 0; i < list->length; i++) {\n", ofst);
|
||||
fprintf(out, "%s%smemcpy(&m_%s[i], list->items[i], sizeof (*m_%s));\n", ofst,
|
||||
ofst, opts->name, opts->name);
|
||||
fprintf(out, "%s}\n", ofst);
|
||||
fprintf(out, "%smemset(&m_%s[i], 0, sizeof (m_%s[i]));\n", ofst, opts->name,
|
||||
opts->name);
|
||||
fprintf(out, "%sreturn i;\n", ofst);
|
||||
fprintf(out, "}\n\n");
|
||||
|
||||
|
||||
fprintf(out, "#else\n\n");
|
||||
|
||||
for (i = 0; i < list->length; i++) {
|
||||
struct ax_html *html = list->items[i];
|
||||
if (html->type == AX_HTML_BLOCK) {
|
||||
fprintf(out, "int\n%s_put_%s(AislStream s, %sCallback callback, "
|
||||
"void *ctx)\n", opts->name, html->data, c_prefix);
|
||||
fprintf(out, "{\n");
|
||||
fprintf(out, "%sreturn put_from(s, callback, ctx, %u);\n", ofst, i + 1);
|
||||
fprintf(out, "}\n\n");
|
||||
}
|
||||
}
|
||||
fprintf(out, "#endif\n\n");
|
||||
|
||||
fclose(out);
|
||||
|
||||
goto finally;
|
||||
|
||||
e_syscall:
|
||||
result = AISL_SYSCALL_ERROR;
|
||||
goto finally;
|
||||
|
||||
e_malloc:
|
||||
result = AISL_MALLOC_ERROR;
|
||||
|
||||
finally:
|
||||
if (c_prefix)
|
||||
free(c_prefix);
|
||||
|
||||
if (u_prefix)
|
||||
free(u_prefix);
|
||||
|
||||
if (f)
|
||||
free(f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ax_html_free(struct ax_html *html)
|
||||
{
|
||||
if (html->data) {
|
||||
free(html->data);
|
||||
}
|
||||
free(html);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file template.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#ifndef AX_HTML_H_92C08532_E423_4CE4_A44C_27CF864A2C60
|
||||
#define AX_HTML_H_92C08532_E423_4CE4_A44C_27CF864A2C60
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <aisl/aisl.h>
|
||||
#include <cStuff/list.h>
|
||||
|
||||
#define AX_HTML_NULL 0
|
||||
#define AX_HTML_PLAIN 1
|
||||
#define AX_HTML_BLOCK 2
|
||||
#define AX_HTML_INPUT 3
|
||||
|
||||
struct ax_html {
|
||||
uint16_t type;
|
||||
uint16_t offset;
|
||||
uint32_t size;
|
||||
char *data;
|
||||
};
|
||||
|
||||
struct ax_html_options {
|
||||
const char *offset;
|
||||
const char *path;
|
||||
const char *name;
|
||||
bool minimize;
|
||||
};
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_html_import(CStuffList list, const char *html_file);
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_html_export(CStuffList list, const struct ax_html_options *opts);
|
||||
|
||||
|
||||
void
|
||||
ax_html_free(struct ax_html *html);
|
||||
|
||||
|
||||
#endif /* !HTML_H */
|
||||
@@ -0,0 +1,245 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file passwd.c
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <cStuff/file.h>
|
||||
#include "passwd.h"
|
||||
|
||||
|
||||
struct ax_passwd_ctx {
|
||||
struct ax_passwd *items;
|
||||
int count;
|
||||
};
|
||||
|
||||
|
||||
static bool
|
||||
ax_passwd_hex2bytes(const char *hex, char *out, int max_len)
|
||||
{
|
||||
char s_byte[] = {'\0', '\0', '\0'}, *e_ptr = NULL;
|
||||
for (i = 0; i < max_len; i++) {
|
||||
if (!(*ptr)) {
|
||||
return false;
|
||||
} else {
|
||||
memcpy(s_byte, hex, 2);
|
||||
*out = strtol(s_byte, &e_ptr, 16) & 0xFF;
|
||||
if (e_ptr != &s_byte[2]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (!(*ptr)) ? true : flase;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ax_passwd_get_hash(struct ax_passwd *entry, char *hash, int length)
|
||||
{
|
||||
char *ptr, *saveptr = NULL, *out, c;
|
||||
int token = 0, i, cnt;
|
||||
|
||||
while ((ptr = strtok(hash, "$", &saveptr)) != NULL) {
|
||||
switch (token++) {
|
||||
case 0:
|
||||
if (!(*ptr)) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
if (!(strcmp(ptr, "1"))) {
|
||||
line = NULL;
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
if (ax_passwd_hex2bytes(ptr, entry->salt, sizeof (entry->salt))) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
if (ax_passwd_hex2bytes(ptr, entry->hash, sizeof (entry->hash))) {
|
||||
return 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ax_passwd_get_line(char *line, ssize_t len, void *p_ctx)
|
||||
{
|
||||
int token = 0;
|
||||
struct ax_passwd_ctx *ctx = (struct ax_passwd_ctx *)p_ctx;
|
||||
struct ax_passwd entry = {0};
|
||||
char *ptr, *saveptr = NULL;
|
||||
|
||||
while ((ptr = strtok(line, ":", &saveptr)) != NULL) {
|
||||
switch (token++) {
|
||||
case 0:
|
||||
if (!cstuff_strncpy(&entry.name, ptr, (int)(saveptr - line))) {
|
||||
line = NULL;
|
||||
continue;
|
||||
} else {
|
||||
return CSTUFF_MALLOC_ERROR;
|
||||
}
|
||||
case 1:
|
||||
entry.uid = strtol(ptr, NULL, 10);
|
||||
continue;
|
||||
case 2:
|
||||
entry.gid = strtol(ptr, NULL, 10);
|
||||
continue;
|
||||
case 3:
|
||||
return ax_passwd_get_hash(&entry, ptr, (int)(saveptr - line));
|
||||
default:
|
||||
break;
|
||||
};
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ax_passwd_import(struct ax_passwd **p_out, const char *file)
|
||||
{
|
||||
struct ax_passwd_ctx ctx = {NULL, 0};
|
||||
|
||||
if (!cstuff_file_get_lines(file, ax_passwd_get_line, (void *)&ctx)) {
|
||||
*p_out = ctx->items;
|
||||
return ctx->count;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ax_passwd_export(const struct ax_passwd *in, int size, const char *file)
|
||||
{
|
||||
int result = -1, i, j = 0;
|
||||
char *swap, hash[2 * (sizeof (in->salt) + sizeof (in->hash)) + 2];
|
||||
FILE *f;
|
||||
|
||||
for (i = 0; i < sizeof (in->salt); i++) {
|
||||
sprintf(&hash[j], "%02x", in->salt[i] & 0xFF);
|
||||
j += 2;
|
||||
}
|
||||
hash[j++] = '$';
|
||||
for (i = 0; i < sizeof (in->hash); i++) {
|
||||
sprintf(&hash[j], "%02x", in->hash[i] & 0xFF);
|
||||
j += 2;
|
||||
}
|
||||
|
||||
if (!cstuff_printf(&swap, "%s.swap", file)) {
|
||||
if ((f = fopen(swap, "w")) != NULL) {
|
||||
result = 0;
|
||||
while (size--) {
|
||||
if (fprintf(f, "%s:%"PRIu32":%"PRIu32":$1$%s:\n", in->name, in->uid,
|
||||
in->gid, hash) < 0) {
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
in++;
|
||||
}
|
||||
fclose(f);
|
||||
if (!result) {
|
||||
if (cstuff_file_move(swap, file) != CSTUFF_SUCCESS)
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
free(swap);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ax_passwd_get_salted(unsigned char **out, const char *password,
|
||||
unsigned char *salt)
|
||||
{
|
||||
int l = strlen(password), result = l + AX_PASSWD_SALT_SIZE;
|
||||
unsigned char *salted;
|
||||
|
||||
if ((salted = malloc(result)) != NULL) {
|
||||
strcpy(salted, password);
|
||||
memcpy(&salted[l], salt, AX_PASSWD_SALT_SIZE);
|
||||
return result;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ax_passwd_verify(const struct ax_passwd *passwd, const char *password)
|
||||
{
|
||||
SHA1_CTX ctx;
|
||||
unsigned char *sltpass, hash[AX_PASSWD_HASH_SIZE];
|
||||
int l;
|
||||
|
||||
if ((l = ax_passwd_get_salted(&sltpass, password, passwd->salt)) != -1) {
|
||||
SHA1_Init(&ctx);
|
||||
SHA1_Update(&ctx, sltpass, l);
|
||||
SHA1_Final(hash, &ctx);
|
||||
free(sltpass);
|
||||
return (!memcmp(passwd->hash, hash, sizeof (hash))) ? 0 : 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ax_passwd_set_hash(struct ax_passwd *out, const char *password)
|
||||
{
|
||||
SHA1_CTX ctx;
|
||||
unsigned char *sltpass;
|
||||
int l;
|
||||
|
||||
RAND_bytes(out->salt, (sizeof (out->salt)));
|
||||
|
||||
if ((l = ax_passwd_get_salted(&sltpass, password, out->salt)) != -1) {
|
||||
SHA1_Init(&ctx);
|
||||
SHA1_Update(&ctx, sltpass, l);
|
||||
SHA1_Final(out->hash, &ctx);
|
||||
free(sltpass);
|
||||
return 0;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ax_passwd_free(struct ax_passwd **p_out, int size)
|
||||
{
|
||||
struct ax_passwd *p = *p_out;
|
||||
|
||||
while (size--) {
|
||||
if (p->name)
|
||||
free(p->name);
|
||||
if (p->hash)
|
||||
free(p->hash);
|
||||
p++;
|
||||
}
|
||||
free(*p_out);
|
||||
*p_out = NULL;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file passwd.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#ifndef AX_PASSWD_H_0DE6E254_27E2_4DEF_A88A_4EE84D5C00DA
|
||||
#define AX_PASSWD_H_0DE6E254_27E2_4DEF_A88A_4EE84D5C00DA
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#define AX_PASSWD_TOKENS 4
|
||||
#define AX_PASSWD_SALT_SIZE 16
|
||||
#define AX_PASSWD_HASH_SIZE SHA1_DIGEST_LENGTH
|
||||
|
||||
struct ax_passwd {
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
char *name;
|
||||
char salt[AX_PASSWD_SALT_SIZE];
|
||||
char hash[AX_PASSWD_HASH_SIZE];
|
||||
};
|
||||
|
||||
typedef struct ax_passwd * AxPasswd;
|
||||
|
||||
|
||||
int
|
||||
ax_passwd_import(struct ax_passwd **p_out, const char *file);
|
||||
|
||||
|
||||
int
|
||||
ax_passwd_export(const struct ax_passwd *in, int size, const char *file);
|
||||
|
||||
|
||||
int
|
||||
ax_passwd_verify(const struct ax_passwd *passwd, const char *password);
|
||||
|
||||
|
||||
int
|
||||
ax_passwd_set_hash(struct ax_passwd *out, const char *password);
|
||||
|
||||
|
||||
int
|
||||
ax_passwd_free(struct ax_passwd **p_out, int size);
|
||||
|
||||
#endif /* !AX_PASSWD_H */
|
||||
@@ -152,3 +152,26 @@ ax_query_feed(AxQuery query, const char *data, int32_t length)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ax_query__decode(char *e_str)
|
||||
{
|
||||
char *p = e_str,
|
||||
hex[3] = {0,0,0};
|
||||
do {
|
||||
switch(*e_str) {
|
||||
case '%':
|
||||
hex[0] = *(++e_str);
|
||||
hex[1] = *(++e_str);
|
||||
*p++ = (char)strtol(hex, NULL, 16);
|
||||
break;
|
||||
|
||||
case '+':
|
||||
*p++ = ' ';
|
||||
break;
|
||||
|
||||
default:
|
||||
*p++ = *e_str;
|
||||
}
|
||||
} while(*e_str++ != 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,4 +51,7 @@ AislStatus
|
||||
ax_query_feed(AxQuery query, const char *data, int32_t length);
|
||||
|
||||
|
||||
void
|
||||
ax_query__decode(char *e_str);
|
||||
|
||||
#endif /* !QUERY_H */
|
||||
|
||||
@@ -0,0 +1,524 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file todo.c
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uuid.h>
|
||||
#include <errno.h>
|
||||
#include <cStuff/string.h>
|
||||
#include <cStuff/file.h>
|
||||
#include <components/log.h>
|
||||
#include "todo.h"
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_todo_init(AxTodo self, const char *file)
|
||||
{
|
||||
uuid_generate(self->list_id);
|
||||
|
||||
self->file = file;
|
||||
|
||||
if (cstuff_sprintf(&self->swap, "%s.swap", file) == -1)
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ax_todo_release(AxTodo self)
|
||||
{
|
||||
if (self->swap)
|
||||
free(self->swap);
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
ax_todo_add_project(CStuffList list, time_t completed, char *key, size_t k_len)
|
||||
{
|
||||
int i;
|
||||
AxTodoProject proj;
|
||||
|
||||
for (i = 0; i < list->length; i++) {
|
||||
proj = list->items[i];
|
||||
|
||||
if (strncmp(proj->name, key, k_len) == 0 && !proj->name[k_len]) {
|
||||
proj->ref_count++;
|
||||
|
||||
if (proj->last_completed < completed) {
|
||||
proj->last_completed = completed;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(proj = calloc(1, sizeof (*proj))))
|
||||
return false;
|
||||
|
||||
if (cstuff_strncpy(&proj->name, key, k_len) != -1) {
|
||||
proj->ref_count = 1;
|
||||
proj->last_completed = completed;
|
||||
uuid_generate(proj->project_id);
|
||||
if (cstuff_list_append(list, proj) != -1)
|
||||
return true;
|
||||
}
|
||||
|
||||
ax_todo_project_free(proj);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
ax_todo_add_tag(CStuffList list, char *key, size_t k_len)
|
||||
{
|
||||
int i;
|
||||
AxTodoTag tag;
|
||||
|
||||
for (i = 0; i < list->length; i++) {
|
||||
tag = list->items[i];
|
||||
|
||||
if (strncmp(tag->name, key, k_len) == 0 && !tag->name[k_len]) {
|
||||
tag->ref_count++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(tag = calloc(1, sizeof (*tag))))
|
||||
return false;
|
||||
|
||||
if (cstuff_strncpy(&tag->name, key, k_len) != -1) {
|
||||
tag->ref_count = 1;
|
||||
uuid_generate(tag->tag_id);
|
||||
if (cstuff_list_append(list, tag) != -1)
|
||||
return true;
|
||||
}
|
||||
|
||||
ax_todo_tag_free(tag);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
ax_todo_read_keys(AxTodoTask task, CStuffList projs, CStuffList tags)
|
||||
{
|
||||
const char tokens[] = "+@";
|
||||
char *s = task->description;
|
||||
size_t offset = 0;
|
||||
|
||||
for (;;) {
|
||||
char t;
|
||||
offset = strcspn(s, tokens);
|
||||
|
||||
s += offset;
|
||||
|
||||
if ((t = *(s++)) == 0)
|
||||
break;
|
||||
|
||||
offset = strcspn(s, " ");
|
||||
|
||||
if (offset) {
|
||||
if ((t == '+' && !ax_todo_add_project(projs, task->completed, s, offset))
|
||||
|| (t == '@' && !ax_todo_add_tag(tags, s, offset))) {
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
s += offset;
|
||||
}
|
||||
}
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_todo_read(AxTodo self, CStuffList tasks, CStuffList projs, CStuffList tags)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
size_t sz = 256;
|
||||
char *buf;
|
||||
ssize_t rs;
|
||||
AislStatus result = AISL_SUCCESS;
|
||||
|
||||
if (!(buf = malloc(sz)))
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
if (!(f = fopen(self->file, "r")))
|
||||
goto e_syscall;
|
||||
|
||||
while ((rs = getdelim(&buf, &sz, '\n', f)) != -1) {
|
||||
AxTodoTask task;
|
||||
|
||||
if (!(task = ax_todo_task_new(self->list_id)))
|
||||
goto e_malloc;
|
||||
|
||||
if (ax_todo_task_set(task, buf, rs) != 0) {
|
||||
ax_todo_task_free(task);
|
||||
goto e_input;
|
||||
}
|
||||
|
||||
if (cstuff_list_append(tasks, task) == -1) {
|
||||
ax_todo_task_free(task);
|
||||
goto e_malloc;
|
||||
}
|
||||
|
||||
if ((result = ax_todo_read_keys(task, projs, tags)) != AISL_SUCCESS) {
|
||||
goto e_malloc;
|
||||
}
|
||||
}
|
||||
|
||||
switch(errno) {
|
||||
case 0:
|
||||
goto finally;
|
||||
|
||||
case ENOMEM:
|
||||
goto e_malloc;
|
||||
|
||||
default:
|
||||
goto e_syscall;
|
||||
}
|
||||
|
||||
e_input:
|
||||
result = AISL_INPUT_ERROR;
|
||||
goto finally;
|
||||
|
||||
e_malloc:
|
||||
result = AISL_MALLOC_ERROR;
|
||||
goto finally;
|
||||
|
||||
e_syscall:
|
||||
result = AISL_SYSCALL_ERROR;
|
||||
|
||||
finally:
|
||||
free(buf);
|
||||
if (f)
|
||||
fclose(f);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ax_todo__write_dt(FILE *f, time_t dt)
|
||||
{
|
||||
char str_dt[16];
|
||||
struct tm *p_stm = gmtime(&dt);
|
||||
(void) strftime(str_dt, sizeof (str_dt), "%Y-%m-%d", p_stm);
|
||||
fprintf(f, "%s ", str_dt);
|
||||
}
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_todo_write(AxTodo self, CStuffList list)
|
||||
{
|
||||
FILE *f;
|
||||
int i;
|
||||
|
||||
if (!(f = fopen(self->swap, "w")))
|
||||
return AISL_SYSCALL_ERROR;
|
||||
|
||||
for (i = 0; i < list->length; i++) {
|
||||
AxTodoTask task = (AxTodoTask) list->items[i];
|
||||
|
||||
if (uuid_compare(task->list_id, self->list_id) != 0)
|
||||
continue;
|
||||
|
||||
if (task->is_done) {
|
||||
fwrite("x ", 1, 2, f);
|
||||
}
|
||||
|
||||
if (task->priority && task->priority != AX_TODO_ZERO_PRIORITY) {
|
||||
fprintf(f, "(%c) ", task->priority);
|
||||
}
|
||||
|
||||
if (task->is_done) {
|
||||
if (task->completed) {
|
||||
ax_todo__write_dt(f, task->completed);
|
||||
if (task->created) {
|
||||
ax_todo__write_dt(f, task->created);
|
||||
}
|
||||
}
|
||||
} else if (task->created) {
|
||||
ax_todo__write_dt(f, task->created);
|
||||
}
|
||||
|
||||
fprintf(f, "%s\n", task->description);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
if (cstuff_file_move(self->swap, self->file) != 0)
|
||||
return AISL_SYSCALL_ERROR;
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
AxTodoTask
|
||||
ax_todo_task_new(uuid_t list_id)
|
||||
{
|
||||
AxTodoTask result;
|
||||
|
||||
if ((result = calloc(1, sizeof (*result))) != NULL) {
|
||||
uuid_copy(result->list_id, list_id);
|
||||
uuid_generate(result->task_id);
|
||||
result->priority = AX_TODO_ZERO_PRIORITY;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ax_todo_task_release(AxTodoTask task)
|
||||
{
|
||||
if (task->description)
|
||||
free(task->description);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ax_todo_task_free(AxTodoTask task)
|
||||
{
|
||||
ax_todo_task_release(task);
|
||||
free(task);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ax_todo__get_priority(char *buf, char *p_priority)
|
||||
{
|
||||
int result;
|
||||
char priority = 0;
|
||||
const char fmt[] = {'(', 'A', ')', ' '};
|
||||
|
||||
for (result = 0; result < 4; result++) {
|
||||
switch (result) {
|
||||
case 0:
|
||||
case 2:
|
||||
case 3:
|
||||
if (buf[result] == fmt[result])
|
||||
continue;
|
||||
break;
|
||||
case 1:
|
||||
priority = buf[result];
|
||||
if (!(priority < 0x41 || priority > 0x5A))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
*p_priority = priority;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ax_todo__get_dt(char *buf, time_t *p_dt)
|
||||
{
|
||||
struct tm stm = {0};
|
||||
int result, *p_num = NULL, b_hour = 0, b_min = 0, usec = 0, z_bias = 0;
|
||||
const char fmt[] = "Y-M-DTH:m:s.uZB:b ", *f = fmt;
|
||||
char *ptr = NULL;
|
||||
|
||||
setenv("TZ", "UTC", 1);
|
||||
|
||||
for (result = 0; *f != 0; result++, f++) {
|
||||
switch(*f) {
|
||||
case 'Y':
|
||||
p_num = &stm.tm_year;
|
||||
break;
|
||||
case 'M':
|
||||
p_num = &stm.tm_mon;
|
||||
break;
|
||||
case 'D':
|
||||
p_num = &stm.tm_mday;
|
||||
break;
|
||||
case 'H':
|
||||
p_num = &stm.tm_hour;
|
||||
break;
|
||||
case 'm':
|
||||
p_num = &stm.tm_min;
|
||||
break;
|
||||
case 's':
|
||||
p_num = &stm.tm_sec;
|
||||
break;
|
||||
case 'u':
|
||||
p_num = &usec;
|
||||
break;
|
||||
case 'B':
|
||||
p_num = &b_hour;
|
||||
break;
|
||||
case 'b':
|
||||
p_num = &b_min;
|
||||
break;
|
||||
|
||||
case '.':
|
||||
if (buf[result] != *f) {
|
||||
f++;
|
||||
}
|
||||
continue;
|
||||
|
||||
case 'T':
|
||||
if (buf[result] == *f)
|
||||
continue;
|
||||
|
||||
if (buf[result] == ' ') {
|
||||
f = &fmt[17];
|
||||
continue;
|
||||
}
|
||||
goto e_input;
|
||||
|
||||
case 'Z':
|
||||
if (buf[result] == *f) {
|
||||
f += 3;
|
||||
z_bias = 'Z';
|
||||
} else if (buf[result] == '-') {
|
||||
z_bias = -1;
|
||||
} else if (buf[result] == '+') {
|
||||
z_bias = 1;
|
||||
} else {
|
||||
goto e_input;
|
||||
}
|
||||
continue;
|
||||
|
||||
case '-':
|
||||
case ':':
|
||||
case ' ':
|
||||
if (buf[result] == *f)
|
||||
continue;
|
||||
/* go through */
|
||||
default:
|
||||
goto e_input;
|
||||
} /* switch */
|
||||
|
||||
*p_num = strtol(&buf[result], &ptr, 10);
|
||||
|
||||
if (!ptr || ptr == &buf[result]) {
|
||||
if (f > &fmt[6] && buf[result] == ' ') {
|
||||
f = &fmt[17];
|
||||
/* skip optional part of timestamp */
|
||||
continue;
|
||||
}
|
||||
goto e_input;
|
||||
}
|
||||
|
||||
result += (ptr - &buf[result] - 1);
|
||||
} /* for */
|
||||
|
||||
/* validate results */
|
||||
/* ToDo: improve (stm.tm_mday < 31) check */
|
||||
if (!(stm.tm_year < 1900) && (stm.tm_mon) && (stm.tm_mday) &&
|
||||
(stm.tm_mon < 13) && (stm.tm_mday < 31) && (stm.tm_hour < 24) &&
|
||||
(stm.tm_min < 60) && (stm.tm_sec < 60)) {
|
||||
stm.tm_year -= 1900;
|
||||
stm.tm_mon--;
|
||||
|
||||
if (z_bias == 0) { /* local time zone */
|
||||
*p_dt = mktime(&stm);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (z_bias != 'Z') {
|
||||
stm.tm_hour += (b_hour * z_bias);
|
||||
stm.tm_min += (b_min * z_bias);
|
||||
}
|
||||
|
||||
*p_dt = mktime(&stm);
|
||||
unsetenv("TZ");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
e_input:
|
||||
/* AX_LOG_DEBUG("Parse error: %c(%d) at %d", *f, (int) (f - fmt), result); */
|
||||
return 0;
|
||||
(void) usec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
ax_todo__remove_line_break(char *description)
|
||||
{
|
||||
int move = 0;
|
||||
char c;
|
||||
|
||||
do {
|
||||
c = *description;
|
||||
|
||||
if (c == '\r' || c == '\n') {
|
||||
move++;
|
||||
continue;
|
||||
}
|
||||
|
||||
*(description - move) = c;
|
||||
|
||||
} while (c != '\0');
|
||||
}
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_todo_task_set(AxTodoTask task, char *buf, ssize_t length)
|
||||
{
|
||||
bool is_done;
|
||||
int l;
|
||||
|
||||
if (strncmp(buf, "x ", 2) == 0) {
|
||||
is_done = true;
|
||||
buf += 2;
|
||||
} else {
|
||||
is_done = false;
|
||||
}
|
||||
|
||||
task->is_done = is_done;
|
||||
|
||||
if ((l = ax_todo__get_priority(buf, &task->priority)) != 0) {
|
||||
buf += l;
|
||||
}
|
||||
|
||||
l = ax_todo__get_dt(buf, (is_done) ? &task->completed : &task->created);
|
||||
|
||||
if (l != 0) {
|
||||
buf += l;
|
||||
if (is_done && (l = ax_todo__get_dt(buf, &task->created)) != 0) {
|
||||
buf += l;
|
||||
}
|
||||
}
|
||||
|
||||
if ((l = cstuff_strcpy(&task->description, buf)) == -1) {
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
|
||||
if (l > 1 && task->description[l-1] == '\n')
|
||||
task->description[l-1] = 0;
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ax_todo_project_free(AxTodoProject project)
|
||||
{
|
||||
if (project->name)
|
||||
free(project->name);
|
||||
free(project);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ax_todo_tag_free(AxTodoTag tag)
|
||||
{
|
||||
if (tag->name)
|
||||
free(tag->name);
|
||||
free(tag);
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file todo.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief ToDo.txt module
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#ifndef TODO_H_FAD28DF0_DA05_4436_88E5_E3715DA254E8
|
||||
#define TODO_H_FAD28DF0_DA05_4436_88E5_E3715DA254E8
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <aisl/aisl.h>
|
||||
#include <cStuff/list.h>
|
||||
|
||||
#define AX_TODO_ZERO_PRIORITY '_'
|
||||
|
||||
struct ax_todo {
|
||||
uuid_t list_id;
|
||||
const char *file;
|
||||
char *swap;
|
||||
/* todo: add file timestamp */
|
||||
};
|
||||
|
||||
typedef struct ax_todo * AxTodo;
|
||||
|
||||
|
||||
struct ax_todo_task {
|
||||
uuid_t list_id;
|
||||
uuid_t task_id;
|
||||
time_t completed;
|
||||
time_t created;
|
||||
char *description;
|
||||
bool is_done;
|
||||
char priority;
|
||||
};
|
||||
|
||||
typedef struct ax_todo_task * AxTodoTask;
|
||||
|
||||
|
||||
struct ax_todo_project {
|
||||
uuid_t project_id;
|
||||
time_t last_completed;
|
||||
char *name;
|
||||
uint32_t ref_count;
|
||||
};
|
||||
|
||||
typedef struct ax_todo_project * AxTodoProject;
|
||||
|
||||
|
||||
struct ax_todo_tag {
|
||||
uuid_t tag_id;
|
||||
char *name;
|
||||
uint32_t ref_count;
|
||||
};
|
||||
|
||||
typedef struct ax_todo_tag * AxTodoTag;
|
||||
|
||||
AislStatus
|
||||
ax_todo_init(AxTodo self, const char *file);
|
||||
|
||||
|
||||
void
|
||||
ax_todo_release(AxTodo self);
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_todo_read(AxTodo self, CStuffList tasks, CStuffList projs, CStuffList tags);
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_todo_write(AxTodo self, CStuffList list);
|
||||
|
||||
|
||||
AxTodoTask
|
||||
ax_todo_task_new(uuid_t list_id);
|
||||
|
||||
|
||||
void
|
||||
ax_todo_task_release(AxTodoTask task);
|
||||
|
||||
|
||||
void
|
||||
ax_todo_task_free(AxTodoTask task);
|
||||
|
||||
|
||||
int
|
||||
ax_todo__get_dt(char *buf, time_t *p_dt);
|
||||
|
||||
|
||||
void
|
||||
ax_todo__remove_line_break(char *description);
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_todo_task_set(AxTodoTask task, char *buf, ssize_t length);
|
||||
|
||||
|
||||
void
|
||||
ax_todo_project_free(AxTodoProject project);
|
||||
|
||||
|
||||
void
|
||||
ax_todo_tag_free(AxTodoTag tag);
|
||||
|
||||
|
||||
#endif /* !TODO_H */
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <ctype.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include "log.h"
|
||||
#include "validate.h"
|
||||
|
||||
|
||||
@@ -100,8 +101,11 @@ ax_validate_email(const char *value)
|
||||
continue;
|
||||
|
||||
default:
|
||||
if ( isalnum(c) == 0 )
|
||||
if (isalnum(c))
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (int)(i-value)+1;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "context.h"
|
||||
|
||||
|
||||
|
||||
+37
-10
@@ -23,8 +23,7 @@
|
||||
#include "feedback.h"
|
||||
|
||||
|
||||
struct context
|
||||
{
|
||||
struct context {
|
||||
struct ax_context root;
|
||||
struct ax_query qs;
|
||||
struct ax_mail mail;
|
||||
@@ -34,6 +33,8 @@ struct context
|
||||
|
||||
typedef struct context * Context;
|
||||
|
||||
static const char modName[] = "feedback";
|
||||
|
||||
|
||||
static void
|
||||
context_free(Context ctx)
|
||||
@@ -51,23 +52,30 @@ context_free(Context ctx)
|
||||
|
||||
|
||||
static int
|
||||
on_input_var( const char *key, uint32_t k_len,
|
||||
const char *val, uint32_t v_len,
|
||||
on_input_var(const char *key, uint32_t k_len, const char *val, uint32_t v_len,
|
||||
void *p_ctx )
|
||||
{
|
||||
Context ctx = (Context) p_ctx;
|
||||
AxFeedback mod = (AxFeedback)((AxContext)ctx)->mod;
|
||||
char **out = NULL;
|
||||
|
||||
if (!ctx->email && k_len == mod->name_email_length) {
|
||||
if (strncmp(key, mod->name_email, k_len)==0)
|
||||
return (cstuff_strncpy(&ctx->email, val, v_len) == -1);
|
||||
if (strncmp(key, mod->name_email, k_len) == 0) {
|
||||
out = &ctx->email;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ctx->msg && k_len == mod->name_msg_length) {
|
||||
if (strncmp(key, mod->name_msg, k_len)==0)
|
||||
return (cstuff_strncpy(&ctx->msg, val, v_len) == -1);
|
||||
if (strncmp(key, mod->name_msg, k_len)==0) {
|
||||
out = &ctx->msg;
|
||||
}
|
||||
}
|
||||
|
||||
if (out) {
|
||||
if (cstuff_strncpy(out, val, v_len) == -1)
|
||||
return -1;
|
||||
ax_query__decode(*out);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -128,12 +136,25 @@ on_stream_input(AxFeedback mod, const struct aisl_evt_input *evt)
|
||||
static AislStatus
|
||||
on_stream_request(AxFeedback mod, const struct aisl_evt *evt)
|
||||
{
|
||||
int rc;
|
||||
Context ctx = aisl_get_context((AislStream)evt->source);
|
||||
AislStream s = (AislStream)evt->source;
|
||||
|
||||
/* verify input */
|
||||
if (!ctx->email || !ctx->msg || ax_validate_email(ctx->email) != 0)
|
||||
ax_quick_response(s, AISL_HTTP_BAD_REQUEST);
|
||||
if (!ctx->email) {
|
||||
AX_LOG_ALERT("%s: email empty", modName);
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
if (!ctx->msg) {
|
||||
AX_LOG_ALERT("%s: msg empty", modName);
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
if ((rc = ax_validate_email(ctx->email))!= 0) {
|
||||
AX_LOG_ALERT("%s: invalid email at %d", modName, rc);
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
ctx->mail.reply_to = ctx->email;
|
||||
ctx->mail.msg = ctx->msg;
|
||||
@@ -144,6 +165,12 @@ on_stream_request(AxFeedback mod, const struct aisl_evt *evt)
|
||||
|
||||
aisl_set_output_event(s, true); /**< enable output event */
|
||||
|
||||
goto finally;
|
||||
|
||||
bad_request:
|
||||
ax_quick_response(s, AISL_HTTP_BAD_REQUEST);
|
||||
|
||||
finally:
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
+743
@@ -0,0 +1,743 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file todo.c
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief AISL ToDo.txt module code file
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <uuid.h>
|
||||
#include <string.h>
|
||||
#include <components/query.h>
|
||||
#include <components/quick.h>
|
||||
#include <components/log.h>
|
||||
#include <cStuff/string.h>
|
||||
#include <mods/minion.h>
|
||||
|
||||
|
||||
typedef enum {
|
||||
AX_MINION_UNDEFINED
|
||||
, AX_MINION_TODAY
|
||||
, AX_MINION_CREATE_TASK
|
||||
, AX_MINION_READ_TASK
|
||||
, AX_MINION_UPDATE_TASK
|
||||
, AX_MINION_DELETE_TASK
|
||||
} AxMinionMode;
|
||||
|
||||
|
||||
struct context {
|
||||
struct ax_context root;
|
||||
struct ax_query qs;
|
||||
struct ax_todo_task task;
|
||||
|
||||
AxMinionMode mode;
|
||||
};
|
||||
|
||||
typedef struct context * Context;
|
||||
|
||||
|
||||
typedef int
|
||||
(*AxMinionCompare)(void *object1, void *object2);
|
||||
|
||||
static const char modName[] = "minion";
|
||||
|
||||
|
||||
static Context
|
||||
context_new(const char *path, AislHttpMethod method, AxMinion mod)
|
||||
{
|
||||
Context ctx;
|
||||
|
||||
if (!(ctx = (Context)ax_context_new(AX_MODULE(mod))))
|
||||
return ctx;
|
||||
|
||||
if (strcmp(path, "today/") == 0)
|
||||
ctx->mode = AX_MINION_TODAY;
|
||||
else if (strcmp(path, "create/") == 0)
|
||||
ctx->mode = AX_MINION_CREATE_TASK;
|
||||
else if (strcmp(path, "read/") == 0)
|
||||
ctx->mode = AX_MINION_READ_TASK;
|
||||
else if (strcmp(path, "update/") == 0)
|
||||
ctx->mode = AX_MINION_UPDATE_TASK;
|
||||
else if (strcmp(path, "delete/") == 0)
|
||||
ctx->mode = AX_MINION_DELETE_TASK;
|
||||
|
||||
ctx->task.priority = AX_TODO_ZERO_PRIORITY;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
context_free(Context ctx)
|
||||
{
|
||||
ax_query_release(&ctx->qs);
|
||||
ax_todo_task_release(&ctx->task);
|
||||
|
||||
ax_context_free((AxContext)ctx);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ax_minion_compare_tasks(AxTodoTask task1, AxTodoTask task2)
|
||||
{
|
||||
if (task1->is_done) {
|
||||
if (task2->is_done) {
|
||||
if (task1->completed > task2->completed) {
|
||||
return -1;
|
||||
} else if (task1->completed > task2->completed) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (task2->is_done) {
|
||||
return -1;
|
||||
} else {
|
||||
if (task1->priority < task2->priority) {
|
||||
return -1;
|
||||
} else if (task1->priority == task2->priority) {
|
||||
if (task1->created > task2->created) {
|
||||
return -1;
|
||||
} else if (task1->created < task2->created) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ax_minion_compare_projects(AxTodoProject proj1, AxTodoProject proj2)
|
||||
{
|
||||
if (proj1->last_completed < proj2->last_completed) {
|
||||
return -1;
|
||||
} else if (proj1->last_completed > proj2->last_completed) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (proj1->ref_count > proj2->ref_count) {
|
||||
return -1;
|
||||
} else if (proj1->ref_count < proj2->ref_count) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ax_minion_compare_tags(AxTodoTag tag1, AxTodoTag tag2)
|
||||
{
|
||||
if (tag1->ref_count > tag2->ref_count) {
|
||||
return -1;
|
||||
} else if (tag1->ref_count < tag2->ref_count) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ax_minion_sort(CStuffList list, AxMinionCompare compare_objects)
|
||||
{
|
||||
int l = list->length, ld = l - 1, i, j;
|
||||
void **data = list->items, *swap;
|
||||
|
||||
for (i = 0; i < l; i++) {
|
||||
int c = i + 1;
|
||||
for (j = ld; j >= c; j--) {
|
||||
if (compare_objects((AxTodoTask)data[j], (AxTodoTask)data[j - 1]) == -1) {
|
||||
swap = data[j];
|
||||
data[j] = data[j - 1];
|
||||
data[j - 1] = swap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
on_input_dt(const char *val, uint32_t v_len, time_t *p_out)
|
||||
{
|
||||
char str_dt[36 + 1];
|
||||
if (!(*p_out) && !(v_len < 10 || v_len > sizeof (str_dt) - 1)) {
|
||||
strncpy(str_dt, val, v_len);
|
||||
str_dt[v_len] = 0;
|
||||
(void) ax_todo__get_dt(str_dt, p_out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
on_input_uuid(const char *val, uint32_t v_len, uuid_t uuid_out)
|
||||
{
|
||||
char str_uuid[36 + 1];
|
||||
|
||||
if (v_len == 36) {
|
||||
strncpy(str_uuid, val, v_len);
|
||||
str_uuid[v_len] = 0;
|
||||
uuid_parse(str_uuid, uuid_out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
on_input_var(const char *key, uint32_t k_len, const char *val, uint32_t v_len,
|
||||
void *p_ctx)
|
||||
{
|
||||
Context ctx = (Context) p_ctx;
|
||||
/* AxMinion mod = (AxMinion)((AxContext)ctx)->mod; */
|
||||
char **out = NULL;
|
||||
|
||||
if (k_len == 1) {
|
||||
if (*key == 't') {
|
||||
out = &ctx->task.description;
|
||||
} else if (*key == 'x') {
|
||||
ctx->task.is_done = (v_len == 4 && strncmp(val, "true", 4) == 0);
|
||||
} else if (*key == 'p') {
|
||||
if (v_len == 1 && !((*val < 0x41 || *val > 0x5A))) {
|
||||
ctx->task.priority = *val;
|
||||
}
|
||||
}
|
||||
} else if (k_len == 2) {
|
||||
if (strncmp(key, "id", 2) == 0) {
|
||||
on_input_uuid(val, v_len, ctx->task.task_id);
|
||||
} else if (strncmp(key, "li", 2) == 0) {
|
||||
on_input_uuid(val, v_len, ctx->task.list_id);
|
||||
} else if (strncmp(key, "co", 2) == 0) {
|
||||
on_input_dt(val, v_len, &ctx->task.completed);
|
||||
} else if (strncmp(key, "cr", 2) == 0) {
|
||||
on_input_dt(val, v_len, &ctx->task.created);
|
||||
}
|
||||
}
|
||||
|
||||
if (out && !(*out)) {
|
||||
if (cstuff_strncpy(out, val, v_len) == -1)
|
||||
return -1;
|
||||
ax_query__decode(*out);
|
||||
ax_todo__remove_line_break(*out);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
on_stream_open(AxMinion mod, const struct aisl_evt_open *evt)
|
||||
{
|
||||
Context ctx;
|
||||
AislStream s = (AislStream)evt->evt.source;
|
||||
|
||||
if (!(ctx = context_new(&evt->path[AX_MODULE(mod)->ep_length],
|
||||
evt->http_method, mod))) {
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
|
||||
aisl_set_context(s, ctx);
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
on_stream_header(AxMinion mod, const struct aisl_evt_header *evt)
|
||||
{
|
||||
AislStream s = (AislStream)evt->evt.source;
|
||||
Context ctx = aisl_get_context(s);
|
||||
|
||||
AX_LOG_DEBUG("header: %s = %s", evt->key, evt->value);
|
||||
|
||||
if (strcmp(evt->key, "content-length") == 0) {
|
||||
size_t total = strtoll(evt->value, NULL, 10);
|
||||
|
||||
if(ax_query_init(&ctx->qs, total, on_input_var, (void*)ctx) != 0) {
|
||||
ax_quick_response(s, AISL_HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
on_stream_input(AxMinion mod, const struct aisl_evt_input *evt)
|
||||
{
|
||||
Context ctx = aisl_get_context((AislStream)evt->evt.source);
|
||||
|
||||
AX_LOG_DEBUG("input[%d]: %s", evt->size, evt->data);
|
||||
ax_query_feed(&ctx->qs, evt->data, evt->size);
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ax_minion_date2str(time_t tt, char *str_dt, size_t sz)
|
||||
{
|
||||
struct tm *p_tm = gmtime(&tt);
|
||||
strftime(str_dt, sz, "%Y-%m-%d", p_tm);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
ax_minion_today(AxMinion mod, AislStream s, Context ctx)
|
||||
{
|
||||
/* sprint: [{task1}, {task2}]
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
ax_minion_create_task(AxMinion mod, AislStream s, Context ctx)
|
||||
{
|
||||
AxTodoTask task;
|
||||
char str_uuid[36 + 1];
|
||||
|
||||
if (!(ctx->task.description)) {
|
||||
AX_LOG_STATE("%s: !description", modName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uuid_compare(ctx->task.list_id, mod->todo.list_id) != 0) {
|
||||
AX_LOG_STATE("%s: !list_id", modName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(task = ax_todo_task_new(ctx->task.list_id)))
|
||||
goto e_system;
|
||||
|
||||
task->is_done = ctx->task.is_done;
|
||||
task->priority = ctx->task.priority;
|
||||
if (!(task->created = ctx->task.created))
|
||||
time(&task->created);
|
||||
task->completed = ctx->task.completed;
|
||||
task->description = ctx->task.description;
|
||||
uuid_copy(task->list_id, ctx->task.list_id);
|
||||
ctx->task.description = NULL;
|
||||
|
||||
if (cstuff_list_append(&mod->tasks, task) == -1) {
|
||||
ax_todo_task_free(task);
|
||||
goto e_system;
|
||||
}
|
||||
|
||||
if (ax_todo_write(&mod->todo, &mod->tasks) != AISL_SUCCESS)
|
||||
goto e_system;
|
||||
|
||||
if (aisl_header(s, "Content-Type", "text/json") == -1)
|
||||
goto e_reject;
|
||||
|
||||
uuid_unparse_lower(task->list_id, str_uuid);
|
||||
|
||||
if (aisl_printf(s, "{\"id\": \"%s\"}", str_uuid) == -1)
|
||||
goto e_reject;
|
||||
|
||||
if (aisl_flush(s) != AISL_SUCCESS)
|
||||
goto e_reject;
|
||||
|
||||
goto finally;
|
||||
|
||||
e_reject:
|
||||
aisl_reject(s);
|
||||
goto finally;
|
||||
|
||||
e_system:
|
||||
ax_quick_response(s, AISL_HTTP_INTERNAL_SERVER_ERROR);
|
||||
goto finally;
|
||||
|
||||
finally:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ax_minion_read_task(AxMinion mod, AislStream s, Context ctx)
|
||||
{
|
||||
int i;
|
||||
char str_buf[36 + 1];
|
||||
CStuffList lst;
|
||||
|
||||
if (aisl_response(s, AISL_HTTP_OK, AISL_AUTO_LENGTH) != AISL_SUCCESS)
|
||||
goto e_reject;
|
||||
|
||||
if (aisl_header(s, "Content-Type", "text/json") == -1)
|
||||
goto e_reject;
|
||||
|
||||
/* tasks */
|
||||
if (aisl_write(s, "{\"tasks\":[", 10) == -1)
|
||||
goto e_reject;
|
||||
|
||||
lst = &mod->tasks;
|
||||
for (i = 0; i < lst->length; i++) {
|
||||
AxTodoTask task = lst->items[i];
|
||||
int rc;
|
||||
|
||||
if (i != 0 && aisl_write(s, ",", 1) == -1)
|
||||
goto e_reject;
|
||||
|
||||
uuid_unparse_lower(task->task_id, str_buf);
|
||||
|
||||
if (aisl_printf(s, "\n{\"id\":\"%s\",", str_buf) == -1)
|
||||
goto e_reject;
|
||||
|
||||
uuid_unparse_lower(task->list_id, str_buf);
|
||||
if (aisl_printf(s, "\"li\":\"%s\",", str_buf) == -1)
|
||||
goto e_reject;
|
||||
|
||||
if (aisl_printf(s, "\"x\":%s,", task->is_done ? "true" : "false") == -1)
|
||||
goto e_reject;
|
||||
|
||||
rc = aisl_printf(s, "\"p\":\"%c\",", task->priority ? task->priority : '0');
|
||||
if (rc == -1)
|
||||
goto e_reject;
|
||||
|
||||
if (task->is_done && task->completed) {
|
||||
ax_minion_date2str(task->completed, str_buf, sizeof(str_buf));
|
||||
if (aisl_printf(s, "\"co\":\"%s\",", str_buf) == -1)
|
||||
goto e_reject;
|
||||
}
|
||||
|
||||
if (task->created) {
|
||||
ax_minion_date2str(task->created, str_buf, sizeof(str_buf));
|
||||
if (aisl_printf(s, "\"cr\":\"%s\",", str_buf) == -1)
|
||||
goto e_reject;
|
||||
}
|
||||
|
||||
rc = aisl_printf(s, "\"t\":\"%s\"}", task->description);
|
||||
if (rc == -1)
|
||||
goto e_reject;
|
||||
}
|
||||
|
||||
/* projects */
|
||||
if (aisl_write(s, "\n], \"projects\":[", 16) == -1)
|
||||
goto e_reject;
|
||||
|
||||
lst = &mod->projects;
|
||||
for (i = 0; i < lst->length; i++) {
|
||||
AxTodoProject proj = lst->items[i];
|
||||
int rc;
|
||||
|
||||
if (i != 0 && aisl_write(s, ",", 1) == -1)
|
||||
goto e_reject;
|
||||
|
||||
uuid_unparse_lower(proj->project_id, str_buf);
|
||||
|
||||
if (aisl_printf(s, "\n{\"id\":\"%s\",", str_buf) == -1)
|
||||
goto e_reject;
|
||||
|
||||
if (aisl_printf(s, "\"n\":\"%s\",", proj->name) == -1)
|
||||
goto e_reject;
|
||||
|
||||
rc = aisl_printf(s, "\"r\":%"PRIu32"", proj->ref_count);
|
||||
if (rc == -1)
|
||||
goto e_reject;
|
||||
|
||||
if (proj->last_completed) {
|
||||
ax_minion_date2str(proj->last_completed, str_buf, sizeof(str_buf));
|
||||
if (aisl_printf(s, ",\"lc\":\"%s\"", str_buf) == -1)
|
||||
goto e_reject;
|
||||
}
|
||||
|
||||
if (aisl_write(s, "}", 1) == -1)
|
||||
goto e_reject;
|
||||
|
||||
}
|
||||
|
||||
/* tags */
|
||||
if (aisl_write(s, "\n], \"tags\":[", 12) == -1)
|
||||
goto e_reject;
|
||||
|
||||
lst = &mod->tags;
|
||||
for (i = 0; i < lst->length; i++) {
|
||||
AxTodoTag tag = lst->items[i];
|
||||
int rc;
|
||||
|
||||
if (i != 0 && aisl_write(s, ",", 1) == -1)
|
||||
goto e_reject;
|
||||
|
||||
uuid_unparse_lower(tag->tag_id, str_buf);
|
||||
|
||||
if (aisl_printf(s, "\n{\"id\":\"%s\",", str_buf) == -1)
|
||||
goto e_reject;
|
||||
|
||||
if (aisl_printf(s, "\"n\":\"%s\",", tag->name) == -1)
|
||||
goto e_reject;
|
||||
|
||||
rc = aisl_printf(s, "\"r\":%"PRIu32"}", tag->ref_count);
|
||||
if (rc == -1)
|
||||
goto e_reject;
|
||||
}
|
||||
if (aisl_write(s, "]}", 2) == -1)
|
||||
goto e_reject;
|
||||
|
||||
if (aisl_flush(s) != AISL_SUCCESS)
|
||||
goto e_reject;
|
||||
|
||||
return;
|
||||
|
||||
e_reject:
|
||||
AX_LOG_DEBUG("%s: reject", modName);
|
||||
aisl_reject(s);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
ax_minion_update_task(AxMinion mod, AislStream s, Context ctx)
|
||||
{
|
||||
AxTodoTask task = NULL;
|
||||
char str_uuid[36 + 1];
|
||||
int i;
|
||||
|
||||
if (!(ctx->task.description)) {
|
||||
AX_LOG_STATE("%s: !description", modName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uuid_compare(ctx->task.list_id, mod->todo.list_id) != 0) {
|
||||
AX_LOG_STATE("%s: !list_id", modName);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < mod->tasks.length; i++) {
|
||||
task = (AxTodoTask) mod->tasks.items[i];
|
||||
if (uuid_compare(task->task_id, ctx->task.task_id) == 0)
|
||||
break;
|
||||
task = NULL;
|
||||
}
|
||||
|
||||
if (!task) {
|
||||
AX_LOG_STATE("%s: !task_id", modName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(task->priority = ctx->task.priority))
|
||||
task->priority = AX_TODO_ZERO_PRIORITY;
|
||||
task->created = ctx->task.created;
|
||||
task->completed = ctx->task.completed;
|
||||
|
||||
if ((task->is_done = ctx->task.is_done) && !task->completed) {
|
||||
time(&task->completed);
|
||||
}
|
||||
|
||||
if (task->description)
|
||||
free(task->description);
|
||||
task->description = ctx->task.description;
|
||||
ctx->task.description = NULL;
|
||||
uuid_copy(task->list_id, ctx->task.list_id);
|
||||
|
||||
if (ax_todo_write(&mod->todo, &mod->tasks) != AISL_SUCCESS)
|
||||
goto e_system;
|
||||
|
||||
if (aisl_header(s, "Content-Type", "text/json") == -1)
|
||||
goto e_reject;
|
||||
|
||||
uuid_unparse_lower(task->list_id, str_uuid);
|
||||
|
||||
if (aisl_printf(s, "{\"id\": \"%s\"}", str_uuid) == -1)
|
||||
goto e_reject;
|
||||
|
||||
if (aisl_flush(s) != AISL_SUCCESS)
|
||||
goto e_reject;
|
||||
|
||||
goto finally;
|
||||
|
||||
e_reject:
|
||||
aisl_reject(s);
|
||||
goto finally;
|
||||
|
||||
e_system:
|
||||
ax_quick_response(s, AISL_HTTP_INTERNAL_SERVER_ERROR);
|
||||
goto finally;
|
||||
|
||||
finally:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
ax_minion_delete_task(AxMinion mod, AislStream s, Context ctx)
|
||||
{
|
||||
AxTodoTask task = NULL;
|
||||
char str_uuid[36 + 1];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mod->tasks.length; i++) {
|
||||
task = (AxTodoTask) mod->tasks.items[i];
|
||||
if (uuid_compare(task->task_id, ctx->task.task_id) == 0) {
|
||||
cstuff_list_remove(&mod->tasks, i);
|
||||
break;
|
||||
}
|
||||
task = NULL;
|
||||
}
|
||||
|
||||
if (!task) {
|
||||
AX_LOG_STATE("%s: !task_id", modName);
|
||||
return false;
|
||||
}
|
||||
|
||||
uuid_unparse_lower(task->list_id, str_uuid);
|
||||
ax_todo_task_free(task);
|
||||
|
||||
if (ax_todo_write(&mod->todo, &mod->tasks) != AISL_SUCCESS)
|
||||
goto e_system;
|
||||
|
||||
if (aisl_header(s, "Content-Type", "text/json") == -1)
|
||||
goto e_reject;
|
||||
|
||||
if (aisl_printf(s, "{\"id\": \"%s\"}", str_uuid) == -1)
|
||||
goto e_reject;
|
||||
|
||||
if (aisl_flush(s) != AISL_SUCCESS)
|
||||
goto e_reject;
|
||||
|
||||
goto finally;
|
||||
|
||||
e_reject:
|
||||
aisl_reject(s);
|
||||
goto finally;
|
||||
|
||||
e_system:
|
||||
ax_quick_response(s, AISL_HTTP_INTERNAL_SERVER_ERROR);
|
||||
goto finally;
|
||||
|
||||
finally:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
on_stream_request(AxMinion mod, const struct aisl_evt *evt)
|
||||
{
|
||||
Context ctx = aisl_get_context((AislStream)evt->source);
|
||||
AislStream s = (AislStream)evt->source;
|
||||
|
||||
/* verify input */
|
||||
AX_LOG_STATE("%s: request mode = %d", modName, ctx->mode);
|
||||
switch(ctx->mode) {
|
||||
case AX_MINION_TODAY:
|
||||
if (!ax_minion_today(mod, s, ctx))
|
||||
goto bad_request;
|
||||
break;
|
||||
|
||||
case AX_MINION_CREATE_TASK:
|
||||
if (!ax_minion_create_task(mod, s, ctx))
|
||||
goto bad_request;
|
||||
break;
|
||||
|
||||
case AX_MINION_READ_TASK:
|
||||
ax_minion_read_task(mod, s, ctx);
|
||||
break;
|
||||
|
||||
case AX_MINION_UPDATE_TASK:
|
||||
if (!ax_minion_update_task(mod, s, ctx))
|
||||
goto bad_request;
|
||||
break;
|
||||
|
||||
case AX_MINION_DELETE_TASK:
|
||||
if (!ax_minion_delete_task(mod, s, ctx))
|
||||
goto bad_request;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
goto finally;
|
||||
|
||||
bad_request:
|
||||
ax_quick_response(s, AISL_HTTP_BAD_REQUEST);
|
||||
|
||||
finally:
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
on_stream_close(AxMinion mod, const struct aisl_evt *evt)
|
||||
{
|
||||
context_free((Context)aisl_get_context((AislStream)evt->source));
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
ax_minion_on_event(AxMinion mod, const struct aisl_evt *evt)
|
||||
{
|
||||
switch(evt->code) {
|
||||
case AISL_EVENT_STREAM_OPEN:
|
||||
return on_stream_open(mod, (const struct aisl_evt_open *)evt);
|
||||
|
||||
case AISL_EVENT_STREAM_HEADER:
|
||||
return on_stream_header(mod, (const struct aisl_evt_header *)evt);
|
||||
|
||||
case AISL_EVENT_STREAM_INPUT:
|
||||
return on_stream_input(mod, (const struct aisl_evt_input *)evt);
|
||||
|
||||
case AISL_EVENT_STREAM_REQUEST:
|
||||
return on_stream_request(mod, evt);
|
||||
|
||||
case AISL_EVENT_STREAM_CLOSE:
|
||||
return on_stream_close(mod, evt);
|
||||
|
||||
default:
|
||||
return AISL_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_minion_init(AxMinion mod, const struct ax_minion_cfg *cfg)
|
||||
{
|
||||
AislStatus result;
|
||||
|
||||
AX_MODULE_INIT(ax_minion, cfg->end_point);
|
||||
|
||||
if ((result = ax_todo_init(&mod->todo, cfg->todo_file)) != AISL_SUCCESS)
|
||||
return result;
|
||||
|
||||
if ((cstuff_list_init(&mod->tasks, 32)) != CSTUFF_SUCCESS)
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
if ((cstuff_list_init(&mod->projects, 8)) != CSTUFF_SUCCESS)
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
if ((cstuff_list_init(&mod->tags, 32)) != CSTUFF_SUCCESS)
|
||||
return AISL_MALLOC_ERROR;
|
||||
|
||||
if ((result = ax_todo_read(&mod->todo, &mod->tasks, &mod->projects,
|
||||
&mod->tags)) != AISL_SUCCESS)
|
||||
return result;
|
||||
|
||||
ax_minion_sort(&mod->tasks, (AxMinionCompare)ax_minion_compare_tasks);
|
||||
ax_minion_sort(&mod->projects, (AxMinionCompare)ax_minion_compare_projects);
|
||||
ax_minion_sort(&mod->tags, (AxMinionCompare)ax_minion_compare_tags);
|
||||
|
||||
if (ax_todo_write(&mod->todo, &mod->tasks) != AISL_SUCCESS) {
|
||||
AX_LOG_DEBUG("Write failed ;(");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ax_minion_release(AxMinion mod)
|
||||
{
|
||||
ax_todo_release(&mod->todo);
|
||||
cstuff_list_release(&mod->tasks, (CStuffListFree) ax_todo_task_free);
|
||||
cstuff_list_release(&mod->projects, (CStuffListFree) ax_todo_project_free);
|
||||
cstuff_list_release(&mod->tags, (CStuffListFree) ax_todo_tag_free);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file mod-feedback.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief AISL ToDo.txt module header file
|
||||
*
|
||||
* @see https://lowenware.com/aisl/
|
||||
*/
|
||||
|
||||
#ifndef MINION_H2FC5C912_0F59_43AD_B805_B0F7C82BE2EF
|
||||
#define MINION_H2FC5C912_0F59_43AD_B805_B0F7C82BE2EF
|
||||
|
||||
#include <cStuff/list.h>
|
||||
#include <mods/context.h>
|
||||
#include <mods/module.h>
|
||||
#include <components/todo.h>
|
||||
|
||||
/* ToDo:
|
||||
* Start with one todo.txt file
|
||||
* Write ToDo.txt parser
|
||||
* Join them together
|
||||
*/
|
||||
|
||||
struct ax_minion_cfg {
|
||||
const char *end_point;
|
||||
const char *todo_file;
|
||||
};
|
||||
|
||||
|
||||
struct ax_minion {
|
||||
struct ax_module root;
|
||||
struct ax_todo todo;
|
||||
struct cstuff_list tasks;
|
||||
struct cstuff_list projects;
|
||||
struct cstuff_list tags;
|
||||
};
|
||||
|
||||
|
||||
typedef struct ax_minion *AxMinion;
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_minion_init(AxMinion mod, const struct ax_minion_cfg *cfg);
|
||||
|
||||
|
||||
void
|
||||
ax_minion_release(AxMinion mod);
|
||||
|
||||
#endif /* !MINION_H */
|
||||
+1
-1
@@ -46,7 +46,7 @@ struct ax_module {
|
||||
const char *end_point; /**< Root mod's URL or NULL */
|
||||
AxObserver on_event; /**< Mod's stream event observer */
|
||||
size_t ctx_size; /**< Mod's context size */
|
||||
uint8_t ep_length; /**< End-Point length */
|
||||
uint32_t ep_length; /**< End-Point length */
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user