Compare commits

...
10 Commits
Author SHA1 Message Date
elias 6347444494 Add crypt module 2019-07-19 21:19:49 +02:00
elias f43bb30065 Add passwd component 2019-07-17 22:50:08 +02:00
elias e00ffe6f70 Add implementation of html module 2019-07-14 20:55:27 +02:00
elias b9e247b88f Add all core functions for minion module 2019-06-28 15:59:02 +02:00
elias 45b4f6bee0 Add minion module and todo component 2019-06-24 07:42:56 +02:00
elias c9c9bbbff6 Correct code styling 2019-06-24 07:42:25 +02:00
elias cbdf7b2523 Remove cStuff from repository 2019-06-19 21:06:25 +02:00
elias 08e771eaa0 Fix URI decode issue for feedback module 2019-04-29 09:08:18 +02:00
elias e623c0dce1 Fix email validation 2019-04-25 15:04:45 +02:00
elias e3e84a0441 Fix bad request handling 2019-04-25 14:18:29 +02:00
21 changed files with 2662 additions and 371 deletions
-101
View File
@@ -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;
}
-62
View File
@@ -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 */
-28
View File
@@ -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
View File
@@ -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;
}
-61
View File
@@ -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 */
+265
View File
@@ -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;
}
+63
View File
@@ -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 */
+465
View File
@@ -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);
}
+56
View File
@@ -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 */
+245
View File
@@ -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;
}
+57
View File
@@ -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 */
+23
View File
@@ -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);
}
+3
View File
@@ -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 */
+524
View File
@@ -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);
}
+116
View File
@@ -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 */
+5 -1
View File
@@ -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;
+1
View File
@@ -13,6 +13,7 @@
* @see https://lowenware.com/
*/
#include <stdlib.h>
#include "context.h"
+37 -10
View File
@@ -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
View File
@@ -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);
}
+55
View File
@@ -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
View File
@@ -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 */
};