From 820e76108bc85051d64431495d70f27379756cc8 Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Thu, 12 Sep 2019 18:05:01 +0200 Subject: Added liveapi support, and the maar tool (mark all as read) --- CMakeLists.txt | 11 +- liveapi/CMakeLists.txt | 18 +++ liveapi/libuwsc/buffer.h | 311 +++++++++++++++++++++++++++++++++++++++++++++ liveapi/libuwsc/config.h | 39 ++++++ liveapi/libuwsc/log.h | 44 +++++++ liveapi/libuwsc/sha1.h | 42 ++++++ liveapi/libuwsc/ssl.h | 46 +++++++ liveapi/libuwsc/utils.h | 50 ++++++++ liveapi/libuwsc/uwsc.h | 132 +++++++++++++++++++ liveapi/libuwsc/uwsc_lua.h | 53 ++++++++ liveapi/liveapi.c | 224 ++++++++++++++++++++++++++++++++ liveapi/liveapi.h | 15 +++ liveapi/messages.c | 70 ++++++++++ liveapi/messages.h | 8 ++ maar/CMakeLists.txt | 6 + maar/main.c | 54 ++++++++ 16 files changed, 1121 insertions(+), 2 deletions(-) create mode 100644 liveapi/CMakeLists.txt create mode 100644 liveapi/libuwsc/buffer.h create mode 100644 liveapi/libuwsc/config.h create mode 100644 liveapi/libuwsc/log.h create mode 100644 liveapi/libuwsc/sha1.h create mode 100644 liveapi/libuwsc/ssl.h create mode 100644 liveapi/libuwsc/utils.h create mode 100644 liveapi/libuwsc/uwsc.h create mode 100644 liveapi/libuwsc/uwsc_lua.h create mode 100644 liveapi/liveapi.c create mode 100644 liveapi/liveapi.h create mode 100644 liveapi/messages.c create mode 100644 liveapi/messages.h create mode 100644 maar/CMakeLists.txt create mode 100644 maar/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f6d77ab..eed1733 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,13 +13,18 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() -SET (CMAKE_C_COMPILER "clang") -set(CMAKE_C_FLAGS "-Wall -Werror -Wextra -pedantic -D_XOPEN_SOURCE=10000 -D_XOPEN_SOURCE_EXTENDED") +set(CMAKE_C_COMPILER "clang") +set(CMAKE_C_FLAGS "-Wall -Werror -Wextra -pedantic -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=10000 -D_XOPEN_SOURCE_EXTENDED -Wno-variadic-macros") set(CMAKE_C_FLAGS_DEBUG "-O0 -g -ggdb -pg -fsanitize=address") set(CMAKE_C_FLAGS_RELEASE "-O2") set(CMAKE_C_FLAGS_MinSizeRel "-Os") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g -ggdb -pg -fsanitize=address") +option(DEBUG_WEBSOCKETS "whether or not to print all incoming and outgoing websocket communications" OFF) +if(DEBUG_WEBSOCKETS) + add_definitions(-DDEBUG_WEBSOCKETS) +endif() + execute_process(COMMAND git rev-parse HEAD RESULT_VARIABLE GIT_HASH_RESULT OUTPUT_VARIABLE GIT_HASH_FULL) string(STRIP ${GIT_HASH_FULL} GIT_HASH) @@ -47,4 +52,6 @@ include_directories("${PROJECT_SOURCE_DIR}/external/uthash/src") ### Project subdirectories ##### add_subdirectory(close_direct_conversations) add_subdirectory(common) +add_subdirectory(liveapi) +add_subdirectory(maar) add_subdirectory(restapi) diff --git a/liveapi/CMakeLists.txt b/liveapi/CMakeLists.txt new file mode 100644 index 0000000..7f259b6 --- /dev/null +++ b/liveapi/CMakeLists.txt @@ -0,0 +1,18 @@ +list(APPEND CMAKE_ARGS "-DUWSC_LUA_SUPPORT=OFF") +ExternalProject_Add(libuwsc + PREFIX ${CMAKE_BINARY_DIR}/libuwsc + GIT_REPOSITORY https://github.com/zhaojh329/libuwsc + CMAKE_ARGS "${CMAKE_ARGS}" + STEP_TARGETS build + EXCLUDE_FROM_ALL TRUE + ) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libuwsc) + +file(GLOB_RECURSE SOURCES *.c) + +ADD_LIBRARY(liveapi STATIC ${SOURCES}) +add_dependencies(liveapi libuwsc-build) +target_link_libraries(liveapi common) +target_link_libraries(liveapi crypto ev ssl) +target_link_libraries(liveapi ${CMAKE_CURRENT_BINARY_DIR}/../libuwsc/src/libuwsc-build/src/libuwsc.a) +target_link_libraries(liveapi ${CMAKE_CURRENT_BINARY_DIR}/../cjson/src/cjson-build/libcjson.a) diff --git a/liveapi/libuwsc/buffer.h b/liveapi/libuwsc/buffer.h new file mode 100644 index 0000000..6d6abaa --- /dev/null +++ b/liveapi/libuwsc/buffer.h @@ -0,0 +1,311 @@ +/* + * MIT License + * + * Copyright (c) 2019 Jianhui Zhao + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _BUFFER_H +#define _BUFFER_H + +#include +#include +#include +#include +#include +#include + +/* Test for GCC < 2.96 */ +#if __GNUC__ < 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ < 96)) +#define __builtin_expect(x) (x) +#endif + +#ifndef likely +#define likely(x) __builtin_expect(!!(x), 1) +#endif + +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif + +enum { + P_FD_EOF = 0, + P_FD_ERR = -1, + P_FD_PENDING = -2 +}; + +struct buffer { + size_t persistent; /* persistent size */ + uint8_t *head; /* Head of buffer */ + uint8_t *data; /* Data head pointer */ + uint8_t *tail; /* Data tail pointer */ + uint8_t *end; /* End of buffer */ +}; + +int buffer_init(struct buffer *b, size_t size); +int buffer_resize(struct buffer *b, size_t size); +void buffer_free(struct buffer *b); + + +/* Actual data Length */ +static inline size_t buffer_length(const struct buffer *b) +{ + return b->tail - b->data; +} + +/* The total buffer size */ +static inline size_t buffer_size(const struct buffer *b) +{ + return b->end - b->head; +} + +static inline size_t buffer_headroom(const struct buffer *b) +{ + return b->data - b->head; +} + +static inline size_t buffer_tailroom(const struct buffer *b) +{ + return b->end - b->tail; +} + +static inline void *buffer_data(const struct buffer *b) +{ + return b->data; +} + +static inline void buffer_set_persistent_size(struct buffer *b, size_t size) +{ + size_t new_size = getpagesize(); + + while (new_size < size) + new_size <<= 1; + + b->persistent = new_size; +} + +static inline void buffer_check_persistent_size(struct buffer *b) +{ + if (b->persistent > 0 && + buffer_size(b) > b->persistent && buffer_length(b) < b->persistent) + buffer_resize(b, b->persistent); +} + +void *buffer_put(struct buffer *b, size_t len); + +static inline void *buffer_put_zero(struct buffer *b, size_t len) +{ + void *tmp = buffer_put(b, len); + + if (likely(tmp)) + memset(tmp, 0, len); + return tmp; +} + +static inline void *buffer_put_data(struct buffer *b, const void *data, size_t len) +{ + void *tmp = buffer_put(b, len); + + if (likely(tmp)) + memcpy(tmp, data, len); + return tmp; +} + + +static inline int buffer_put_u8(struct buffer *b, uint8_t val) +{ + uint8_t *p = buffer_put(b, 1); + + if (likely(p)) { + *p = val; + return 0; + } + + return -1; +} + +static inline int buffer_put_u16(struct buffer *b, uint16_t val) +{ + uint16_t *p = buffer_put(b, 2); + + if (likely(p)) { + *p = val; + return 0; + } + + return -1; +} + +static inline int buffer_put_u32(struct buffer *b, uint32_t val) +{ + uint32_t *p = buffer_put(b, 4); + + if (likely(p)) { + *p = val; + return 0; + } + + return -1; +} + +static inline int buffer_put_u64(struct buffer *b, uint64_t val) +{ + uint64_t *p = buffer_put(b, 8); + + if (likely(p)) { + *p = val; + return 0; + } + + return -1; +} + +static inline int buffer_put_string(struct buffer *b, const char *s) +{ + size_t len = strlen(s); + char *p = buffer_put(b, len); + + if (likely(p)) { + memcpy(p, s, len); + return 0; + } + + return -1; +} + +int buffer_put_vprintf(struct buffer *b, const char *fmt, va_list ap) __attribute__((format(printf, 2, 0))); +int buffer_put_printf(struct buffer *b, const char *fmt, ...) __attribute__((format(printf, 2, 3))); + +int buffer_put_fd(struct buffer *b, int fd, ssize_t len, bool *eof, + int (*rd)(int fd, void *buf, size_t count, void *arg), void *arg); + +/** + * buffer_truncate - remove end from a buffer + * @b: buffer to alter + * @len: new length + * + * Cut the length of a buffer down by removing data from the tail. If + * the buffer is already under the length specified it is not modified. + */ +static inline void buffer_truncate(struct buffer *b, size_t len) +{ + if (buffer_length(b) > len) { + b->tail = b->data + len; + buffer_check_persistent_size(b); + } +} + + +size_t buffer_pull(struct buffer *b, void *dest, size_t len); + +static inline uint8_t buffer_pull_u8(struct buffer *b) +{ + uint8_t val = 0; + + if (likely(buffer_length(b) > 0)) { + val = b->data[0]; + b->data += 1; + } + + return val; +} + +static inline uint16_t buffer_pull_u16(struct buffer *b) +{ + uint16_t val = 0; + + if (likely(buffer_length(b) > 1)) { + val = *((uint16_t *)b->data); + b->data += 2; + } + + return val; +} + +static inline uint32_t buffer_pull_u32(struct buffer *b) +{ + uint32_t val = 0; + + if (likely(buffer_length(b) > 3)) { + val = *((uint32_t *)b->data); + b->data += 4; + } + + return val; +} + +static inline uint64_t buffer_pull_u64(struct buffer *b) +{ + uint64_t val = 0; + + if (likely(buffer_length(b) > 7)) { + val = *((uint64_t *)b->data); + b->data += 8; + } + + return val; +} + +static inline uint8_t buffer_get_u8(struct buffer *b, ssize_t offset) +{ + uint8_t val = 0; + + if (likely(buffer_length(b) > (size_t)offset)) + val = b->data[offset]; + + return val; +} + +static inline uint16_t buffer_get_u16(struct buffer *b, ssize_t offset) +{ + uint16_t val = 0; + + if (likely(buffer_length(b) > (size_t)offset + 1)) + val = *((uint16_t *)(b->data + offset)); + + return val; +} + +static inline uint32_t buffer_get_u32(struct buffer *b, ssize_t offset) +{ + uint32_t val = 0; + + if (likely(buffer_length(b) > (size_t)offset + 3)) + val = *((uint32_t *)(b->data + offset)); + + return val; +} + +static inline uint64_t buffer_get_u64(struct buffer *b, ssize_t offset) +{ + uint64_t val = 0; + + if (likely(buffer_length(b) > (size_t)offset + 7)) + val = *((uint64_t *)(b->data + offset)); + + return val; +} + +int buffer_pull_to_fd(struct buffer *b, int fd, size_t len, + int (*wr)(int fd, void *buf, size_t count, void *arg), void *arg); + +void buffer_hexdump(struct buffer *b, size_t offset, size_t len); + +#endif diff --git a/liveapi/libuwsc/config.h b/liveapi/libuwsc/config.h new file mode 100644 index 0000000..c133235 --- /dev/null +++ b/liveapi/libuwsc/config.h @@ -0,0 +1,39 @@ +/* + * MIT License + * + * Copyright (c) 2019 Jianhui Zhao + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _UWSC_CONFIG_H +#define _UWSC_CONFIG_H + +#define UWSC_VERSION_MAJOR 3 +#define UWSC_VERSION_MINOR 3 +#define UWSC_VERSION_PATCH 2 +#define UWSC_VERSION_STRING "3.3.2" + +#define UWSC_SSL_SUPPORT 1 + +#define UWSC_HAVE_OPENSSL 1 +#define UWSC_HAVE_WOLFSSL 0 +#define UWSC_HAVE_MBEDTLS 0 + +#endif diff --git a/liveapi/libuwsc/log.h b/liveapi/libuwsc/log.h new file mode 100644 index 0000000..60d500f --- /dev/null +++ b/liveapi/libuwsc/log.h @@ -0,0 +1,44 @@ +/* + * MIT License + * + * Copyright (c) 2019 Jianhui Zhao + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _UWSC_LOG_H +#define _UWSC_LOG_H + +#include +#include + +void uwsc_log_threshold(int threshold); +void uwsc_log_close(); + +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) + +#define uwsc_log(priority, fmt...) __uwsc_log(__FILENAME__, __LINE__, priority, fmt) + +#define uwsc_log_debug(fmt...) uwsc_log(LOG_DEBUG, fmt) +#define uwsc_log_info(fmt...) uwsc_log(LOG_INFO, fmt) +#define uwsc_log_err(fmt...) uwsc_log(LOG_ERR, fmt) + +void __uwsc_log(const char *filename, int line, int priority, const char *fmt, ...); + +#endif diff --git a/liveapi/libuwsc/sha1.h b/liveapi/libuwsc/sha1.h new file mode 100644 index 0000000..447204c --- /dev/null +++ b/liveapi/libuwsc/sha1.h @@ -0,0 +1,42 @@ +/* + * Modified from mongoose(https://github.com/cesanta/mongoose) + * + * MIT License + * + * Copyright (c) 2019 Jianhui Zhao + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _SHA1_H +#define _SHA1_H + +#include + +struct sha1_ctx { + uint32_t state[5]; + size_t count[2]; + uint8_t buffer[64]; +}; + +void sha1_init(struct sha1_ctx *ctx); +void sha1_update(struct sha1_ctx *ctx, const void *data, size_t len); +void sha1_final(struct sha1_ctx *ctx, uint8_t digest[20]); + +#endif diff --git a/liveapi/libuwsc/ssl.h b/liveapi/libuwsc/ssl.h new file mode 100644 index 0000000..8a8a533 --- /dev/null +++ b/liveapi/libuwsc/ssl.h @@ -0,0 +1,46 @@ +/* + * MIT License + * + * Copyright (c) 2019 Jianhui Zhao + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _UWSC_SSL_H +#define _UWSC_SSL_H + +#include +#include + +#include "config.h" + +#if UWSC_SSL_SUPPORT + +struct uwsc_ssl_ctx; + +int uwsc_ssl_init(struct uwsc_ssl_ctx **ctx, int sock); +int uwsc_ssl_handshake(struct uwsc_ssl_ctx *ctx); +void uwsc_ssl_free(struct uwsc_ssl_ctx *ctx); + +int uwsc_ssl_read(int fd, void *buf, size_t count, void *arg); +int uwsc_ssl_write(int fd, void *buf, size_t count, void *arg); + +#endif + +#endif diff --git a/liveapi/libuwsc/utils.h b/liveapi/libuwsc/utils.h new file mode 100644 index 0000000..dacfa31 --- /dev/null +++ b/liveapi/libuwsc/utils.h @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2019 Jianhui Zhao + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _UTILS_H +#define _UTILS_H + +#include +#include +#include + +#include "config.h" + +#ifndef container_of +#define container_of(ptr, type, member) \ + ({ \ + const __typeof__(((type *) NULL)->member) *__mptr = (ptr); \ + (type *) ((char *) __mptr - offsetof(type, member)); \ + }) +#endif + +int get_nonce(uint8_t *dest, int len); +int parse_url(const char *url, char *host, int host_len, + int *port, const char **path, bool *ssl); + +int tcp_connect(const char *host, int port, int flags, bool *inprogress, int *eai); + +int b64_encode(const void *src, size_t srclen, void *dest, size_t destsize); + +#endif diff --git a/liveapi/libuwsc/uwsc.h b/liveapi/libuwsc/uwsc.h new file mode 100644 index 0000000..72ba2d8 --- /dev/null +++ b/liveapi/libuwsc/uwsc.h @@ -0,0 +1,132 @@ +/* + * MIT License + * + * Copyright (c) 2019 Jianhui Zhao + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _UWSC_H +#define _UWSC_H + +#include + +#include "log.h" +#include "config.h" +#include "buffer.h" + +#define HTTP_HEAD_LIMIT 4096 +#define UWSC_MAX_CONNECT_TIME 5 /* second */ +#define UWSC_BUFFER_PERSISTENT_SIZE 4096 + +/* WebSocket close status codes defined in RFC 6455, section 11.7 */ +enum { + UWSC_CLOSE_STATUS_NORMAL = 1000, + UWSC_CLOSE_STATUS_GOINGAWAY = 1001, + UWSC_CLOSE_STATUS_PROTOCOL_ERR = 1002, + UWSC_CLOSE_STATUS_UNACCEPTABLE_OPCODE = 1003, + UWSC_CLOSE_STATUS_RESERVED = 1004, + UWSC_CLOSE_STATUS_NO_STATUS = 1005, + UWSC_CLOSE_STATUS_ABNORMAL_CLOSE = 1006, + UWSC_CLOSE_STATUS_INVALID_PAYLOAD = 1007, + UWSC_CLOSE_STATUS_POLICY_VIOLATION = 1008, + UWSC_CLOSE_STATUS_MESSAGE_TOO_LARGE = 1009, + UWSC_CLOSE_STATUS_EXTENSION_REQUIRED = 1010, + UWSC_CLOSE_STATUS_UNEXPECTED_CONDITION = 1011, + UWSC_CLOSE_STATUS_TLS_FAILURE = 1015 +}; + +enum { + UWSC_ERROR_IO = 1, + UWSC_ERROR_INVALID_HEADER, + UWSC_ERROR_SERVER_MASKED, + UWSC_ERROR_NOT_SUPPORT, + UWSC_ERROR_PING_TIMEOUT, + UWSC_ERROR_CONNECT, + UWSC_ERROR_SSL_HANDSHAKE +}; + +enum { + CLIENT_STATE_CONNECTING, + CLIENT_STATE_SSL_HANDSHAKE, + CLIENT_STATE_HANDSHAKE, + CLIENT_STATE_PARSE_MSG_HEAD, + CLIENT_STATE_PARSE_MSG_PAYLEN, + CLIENT_STATE_PARSE_MSG_PAYLOAD +}; + +enum { + UWSC_OP_CONTINUE = 0x0, + UWSC_OP_TEXT = 0x1, + UWSC_OP_BINARY = 0x2, + UWSC_OP_CLOSE = 0x8, + UWSC_OP_PING = 0x9, + UWSC_OP_PONG = 0xA +}; + +struct uwsc_frame { + uint8_t opcode; + size_t payloadlen; +}; + +struct uwsc_client { + int sock; + int state; + struct ev_loop *loop; + struct ev_io ior; + struct ev_io iow; + struct buffer rb; + struct buffer wb; + struct uwsc_frame frame; + struct ev_timer timer; + bool wait_pong; + int ping_interval; + ev_tstamp start_time; /* Time stamp of begin connect */ + ev_tstamp last_ping; /* Time stamp of last ping */ + int ntimeout; /* Number of timeouts */ + char key[256]; /* Sec-WebSocket-Key */ + void *ssl; /* Context wrap of openssl, wolfssl and mbedtls */ + + void (*onopen)(struct uwsc_client *cl); + void (*set_ping_interval)(struct uwsc_client *cl, int interval); + void (*onmessage)(struct uwsc_client *cl, void *data, size_t len, bool binary); + void (*onerror)(struct uwsc_client *cl, int err, const char *msg); + void (*onclose)(struct uwsc_client *cl, int code, const char *reason); + + int (*send)(struct uwsc_client *cl, const void *data, size_t len, int op); + int (*send_ex)(struct uwsc_client *cl, int op, int num, ...); + int (*send_close)(struct uwsc_client *cl, int code, const char *reason); + void (*ping)(struct uwsc_client *cl); + void (*free)(struct uwsc_client *cl); +}; + +/* + * uwsc_new - creat an uwsc_client struct and connect to server + * @loop: If NULL will use EV_DEFAULT + * @url: A websock url. ws://xxx.com/xx or wss://xxx.com/xx + * @ping_interval: ping interval + * @extra_header: extra http header. Authorization: a1d4cdb1a3cd6a0e94aa3599afcddcf5\r\n + */ +struct uwsc_client *uwsc_new(struct ev_loop *loop, const char *url, + int ping_interval, const char *extra_header); + +int uwsc_init(struct uwsc_client *cl, struct ev_loop *loop, const char *url, + int ping_interval, const char *extra_header); + +#endif diff --git a/liveapi/libuwsc/uwsc_lua.h b/liveapi/libuwsc/uwsc_lua.h new file mode 100644 index 0000000..9850af7 --- /dev/null +++ b/liveapi/libuwsc/uwsc_lua.h @@ -0,0 +1,53 @@ +/* + * MIT License + * + * Copyright (c) 2019 Jianhui Zhao + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __UWSC_LUA_H +#define __UWSC_LUA_H + +#include +#include + +#include "uwsc.h" + +/* Compatibility defines */ +#if LUA_VERSION_NUM <= 501 + +/* NOTE: this only works if nups == 0! */ +#define luaL_setfuncs(L, fns, nups) luaL_register((L), NULL, (fns)) + +#endif + +struct uwsc_client_lua { + lua_State *L; + + struct uwsc_client cli; + bool connected; + + int onopen_ref; + int onmessage_ref; + int onerror_ref; + int onclose_ref; +}; + +#endif diff --git a/liveapi/liveapi.c b/liveapi/liveapi.c new file mode 100644 index 0000000..b0520ea --- /dev/null +++ b/liveapi/liveapi.c @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include + +#include +#include "libuwsc/uwsc.h" + +#include "common/config.h" +#include "common/util.h" +#include "liveapi.h" +#include "messages.h" +#include "stdin.h" + +static struct uwsc_client * webclient; +static struct ev_loop *loop = NULL; +static char stepping = 1; + +static const char * _authToken; + +struct result { + int id; + cJSON* json; + UT_hash_handle hh; +}; + +static struct result * results = NULL; + +void liveapi_activate_stdin(liveapi_stdin_cb * cb) +{ + liveapi_activate_stdin_with_callback(webclient, cb); +} + +static void liveapi_send(const char* msg) +{ +#ifdef DEBUG_WEBSOCKETS + printf("==>:%s\n", msg); +#endif + webclient->send_ex(webclient, UWSC_OP_TEXT, 1, strlen(msg), msg); + // TODO handle errors +} + +void liveapi_send_json(size_t id, cJSON* json) +{ + char * msg = cJSON_Print(json); + if (id > 0) { + struct result * result = malloc(sizeof(struct result)); + result->id = id; + result->json = json; + HASH_ADD_INT(results, id, result); + } + liveapi_send(msg); + if (id == 0) + cJSON_Delete(json); + free(msg); +} + +static void uwsc_onopen(struct uwsc_client *cl UNUSED) +{ + uwsc_log_info("onopen\n"); +} + +static void uwsc_onmessage(struct uwsc_client *cl, + void *data, size_t len, bool binary) +{ +#ifdef DEBUG_WEBSOCKETS + printf("<==:"); +#endif + + if (binary) { + fprintf(stderr, "Websocket binary messages are not unsupported\n"); +#ifdef DEBUG_WEBSOCKETS + size_t i; + uint8_t *p = data; + + for (i = 0; i < len; i++) { + printf("%02hhX ", p[i]); + if (i % 16 == 0 && i > 0) + puts(""); + } + puts(""); +#endif + } else { +#ifdef DEBUG_WEBSOCKETS + printf("%.*s\n", (int)len, (char *)data); +#endif + char* buffer = malloc (len + 1); + strncpy(buffer, data, len); + buffer[len] = 0; + cJSON* json = cJSON_Parse(buffer); + if (json == NULL) { + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) + fprintf(stderr, "Json parsing error reading websocket message: %s\n", error_ptr); + fprintf(stderr, "Error while parsing websocket message, couldn't parse json output :\n%s\n", buffer); + goto onmessage_json_cleanup; + } + + + const cJSON* server_id = cJSON_GetObjectItemCaseSensitive(json, "server_id"); + if (cJSON_IsString(server_id) && server_id->valuestring != NULL) { + liveapi_connect(); + goto onmessage_json_cleanup; + } + const cJSON* msg = cJSON_GetObjectItemCaseSensitive(json, "msg"); + if (cJSON_IsString(msg) && msg->valuestring != NULL) { + if (strcmp(msg->valuestring, "ping") == 0) { + liveapi_send("{ \"msg\": \"pong\" }"); + } else if (strcmp(msg->valuestring, "connected") == 0) { + liveapi_login(_authToken); + ev_break(cl->loop, EVBREAK_ALL); + } else if (strcmp(msg->valuestring, "result") == 0) { + const cJSON* jid = cJSON_GetObjectItemCaseSensitive(json, "id"); + if (!cJSON_IsString(jid) || jid->valuestring == NULL) { + fprintf(stderr, "Invalid websocket result : %s\n", buffer); + goto onmessage_json_cleanup; + } + size_t id = (size_t) strtoll(jid->valuestring, NULL, 10); + struct result * result = NULL; + HASH_FIND_INT(results, &id, result); + if (result == NULL) { + fprintf(stderr, "Got a websocket result message that doesn't match any method we sent : %s\n", buffer); + goto onmessage_json_cleanup; + } + cJSON_Delete(result->json); + HASH_DEL(results, result); + free(result); + // if we were stepping and got no more results we break out of the loop + if (stepping == 1 && HASH_COUNT(results) == 0) + ev_break(cl->loop, EVBREAK_ALL); + } else { + fprintf(stderr, "Unhandled websocket message : %s\n", msg->valuestring); + } + } else { + fprintf(stderr, "Unhandled websocket without msg : %s\n", buffer); + } +onmessage_json_cleanup: + cJSON_Delete(json); + free(buffer); + } +} + +static void uwsc_onerror(struct uwsc_client *cl, int err, const char *msg) +{ + uwsc_log_err("onerror:%d: %s\n", err, msg); + ev_break(cl->loop, EVBREAK_ALL); +} + +static void uwsc_onclose(struct uwsc_client *cl, int code, const char *reason) +{ + uwsc_log_err("onclose:%d: %s\n", code, reason); + ev_break(cl->loop, EVBREAK_ALL); +} + +static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) +{ + if (w->signum == SIGINT) { + (void) loop; + //ev_break(loop, EVBREAK_ALL); + uwsc_log_info("Normal quit\n"); + webclient->send_close(webclient, 0, "got signal"); + } + (void) revents; +} + +char liveapi_init(const char * authToken) +{ + _authToken = authToken; + + const char *url = config_get_wss_url(); + loop = EV_DEFAULT; + struct ev_signal signal_watcher; + int ping_interval = 10; /* second */ + + uwsc_log_info("Libuwsc: %s\n", UWSC_VERSION_STRING); + + webclient = uwsc_new(loop, url, ping_interval, NULL); + if (!webclient) + return -1; + + uwsc_log_info("Start connect...\n"); + + webclient->onopen = uwsc_onopen; + webclient->onmessage = uwsc_onmessage; + webclient->onerror = uwsc_onerror; + webclient->onclose = uwsc_onclose; + + ev_signal_init(&signal_watcher, signal_cb, SIGINT); + ev_signal_start(loop, &signal_watcher); + + ev_run(loop, 0); + + return 0; +} + +char liveapi_loop(void) +{ + stepping = 0; + ev_run(loop, 0); + return 0; +} + +char liveapi_step(void) +{ + if (HASH_COUNT(results) > 0) { + stepping = 1; + ev_run(loop, 0); + } + return 0; +} + +void liveapi_clean(void) +{ + struct result *s, *tmp; + + HASH_ITER(hh, results, s, tmp) { + cJSON_Delete(s->json); + HASH_DELETE(hh, results, s); + free(s); + } + + free(webclient); +} diff --git a/liveapi/liveapi.h b/liveapi/liveapi.h new file mode 100644 index 0000000..4383c26 --- /dev/null +++ b/liveapi/liveapi.h @@ -0,0 +1,15 @@ +#ifndef LIVEAPI_LIVEAPI_H_ +#define LIVEAPI_LIVEAPI_H_ + +#include + +typedef void (*liveapi_stdin_cb) (const char * input); + +void liveapi_activate_stdin(liveapi_stdin_cb * cb); +void liveapi_send_json(size_t id, cJSON* json); +char liveapi_init(const char * authToken); +char liveapi_loop(void); +char liveapi_step(void); +void liveapi_clean(void); + +#endif diff --git a/liveapi/messages.c b/liveapi/messages.c new file mode 100644 index 0000000..2ef7958 --- /dev/null +++ b/liveapi/messages.c @@ -0,0 +1,70 @@ +#include +#include +#include + +#include +#include "libuwsc/uwsc.h" + +#include "liveapi.h" +#include "messages.h" + +struct methodid { + size_t id; + char sid[33]; +}; + +static const struct methodid* get_next_method_id(void) +{ + static struct methodid methodid = { .id = 1, .sid = "" }; + snprintf(methodid.sid, sizeof(methodid.sid), "%zu", ++methodid.id); + return &methodid; +} + +void liveapi_connect(void) +{ + cJSON* json = cJSON_CreateObject(); + cJSON_AddItemToObject(json, "msg", cJSON_CreateString("connect")); + cJSON_AddItemToObject(json, "version", cJSON_CreateString("1")); + + cJSON* support = cJSON_CreateArray(); + cJSON_AddItemToArray(support, cJSON_CreateString("1")); + cJSON_AddItemToArray(support, cJSON_CreateString("pre2")); + cJSON_AddItemToArray(support, cJSON_CreateString("pre1")); + cJSON_AddItemToObject(json, "support", support); + + liveapi_send_json(0, json); +} + +void liveapi_login(const char * authToken) +{ + const struct methodid * id = get_next_method_id(); + + cJSON* json = cJSON_CreateObject(); + cJSON_AddItemToObject(json, "msg", cJSON_CreateString("method")); + cJSON_AddItemToObject(json, "id", cJSON_CreateString(id->sid)); + cJSON_AddItemToObject(json, "method", cJSON_CreateString("login")); + + cJSON* params = cJSON_CreateArray(); + cJSON* resume = cJSON_CreateObject(); + cJSON_AddItemToObject(resume, "resume", cJSON_CreateString(authToken)); + cJSON_AddItemToArray(params, resume); + cJSON_AddItemToObject(json, "params", params); + + liveapi_send_json(id->id, json); +} + +void liveapi_mark_read(const char * rid) +{ + const struct methodid * id = get_next_method_id(); + + cJSON* json = cJSON_CreateObject(); + cJSON_AddItemToObject(json, "msg", cJSON_CreateString("method")); + cJSON_AddItemToObject(json, "id", cJSON_CreateString(id->sid)); + cJSON_AddItemToObject(json, "method", cJSON_CreateString("readMessages")); + + cJSON* params = cJSON_CreateArray(); + cJSON_AddItemToArray(params, cJSON_CreateString(rid)); + cJSON_AddItemToObject(json, "params", params); + + liveapi_send_json(id->id, json); +} diff --git a/liveapi/messages.h b/liveapi/messages.h new file mode 100644 index 0000000..e8ae539 --- /dev/null +++ b/liveapi/messages.h @@ -0,0 +1,8 @@ +#ifndef LIVEAPI_MESSAGES_H_ +#define LIVEAPI_MESSAGES_H_ + +void liveapi_connect(void); +void liveapi_login(const char * authToken); +void liveapi_mark_read(const char * rid); + +#endif diff --git a/maar/CMakeLists.txt b/maar/CMakeLists.txt new file mode 100644 index 0000000..1062ba1 --- /dev/null +++ b/maar/CMakeLists.txt @@ -0,0 +1,6 @@ +file(GLOB_RECURSE SOURCES *.c) + +ADD_EXECUTABLE(rocket_maar ${SOURCES}) +target_link_libraries(rocket_maar common liveapi restapi) + +install(TARGETS rocket_maar DESTINATION bin) diff --git a/maar/main.c b/maar/main.c new file mode 100644 index 0000000..8ac0225 --- /dev/null +++ b/maar/main.c @@ -0,0 +1,54 @@ +#include +#include + +#include "common/config.h" +#include "common/cli.h" +#include "common/util.h" +#include "liveapi/liveapi.h" +#include "liveapi/messages.h" +#include "restapi/auth.h" +#include "restapi/subscriptions.h" +#include "restapi/users.h" + +void maar_subscription(const struct subscription* subscription) +{ + if (subscription->unread >0) { + printf("%s\n", subscription->name); + liveapi_mark_read(subscription->rid); + liveapi_step(); + } +} + +int main(void) +{ + if (config_load(CONFIG_PATH) != 0) { + return 1; + } + + const char* login = config_get_login(); + if (login == NULL) + login = common_cli_get_login(); + + const char* password = config_get_password(); + if (password == NULL) + password = common_cli_get_password(); + + const char * authToken = restapi_login(login, password); + if (authToken != NULL) { + liveapi_init(authToken); + struct subscription* subscriptions = restapi_subscriptions_get(); + printf("The following active subscriptions are marked as read :\n"); + common_subscriptions_const_walk(subscriptions, &maar_subscription); + common_subscriptions_free(subscriptions); + liveapi_step(); + liveapi_clean(); + restapi_logout(); + } else { + printf("Couldn't init rest api.\n"); + } + + config_clean(); + common_cli_free(); + + return 0; +} -- cgit v1.2.3