aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Dessaux2019-08-04 11:03:38 +0200
committerJulien Dessaux2019-08-05 13:28:51 +0200
commit1bec937076d8d40f94ff4603479f21bca2de6c61 (patch)
treed811f0155c09149e9ade2016e224d58b2fbf6d81
parentAdded configuration file parsing (diff)
downloadrocket-cli-client-1bec937076d8d40f94ff4603479f21bca2de6c61.tar.gz
rocket-cli-client-1bec937076d8d40f94ff4603479f21bca2de6c61.tar.bz2
rocket-cli-client-1bec937076d8d40f94ff4603479f21bca2de6c61.zip
Added http lib and basic restapi with rocket_close_im binary
-rw-r--r--CMakeLists.txt22
-rw-r--r--close_im/CMakeLists.txt8
-rw-r--r--close_im/main.c69
-rw-r--r--close_im/main.cpp46
-rw-r--r--common/config.c18
-rw-r--r--common/config.h.in2
-rw-r--r--common/http.c133
-rw-r--r--common/http.h10
-rw-r--r--common/util.h4
-rw-r--r--restapi/CMakeLists.txt5
-rw-r--r--restapi/auth.c80
-rw-r--r--restapi/auth.h11
-rw-r--r--restapi/im.c58
-rw-r--r--restapi/im.h7
14 files changed, 420 insertions, 53 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ea79a7f..c6b6b2a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.0)
-project(rocket-client LANGUAGES C CXX VERSION 0.0.1)
+project(rocket-client LANGUAGES C VERSION 0.0.1)
set(CMAKE_VERBOSE_MAKEFILE FALSE)
if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
@@ -13,7 +13,8 @@ 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_FLAGS "-Wall -Werror -Wextra -pedantic -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED")
+SET (CMAKE_C_COMPILER "clang")
+set(CMAKE_C_FLAGS "-Wall -Werror -Wextra -pedantic -D_XOPEN_SOURCE=10000 -D_XOPEN_SOURCE_EXTENDED")
set(CMAKE_C_FLAGS_DEBUG "-O0 -g -ggdb -pg -fsanitize=address")
set(CMAKE_C_FLAGS_RELEASE "-O2")
set(CMAKE_C_FLAGS_MinSizeRel "-Os")
@@ -26,4 +27,21 @@ configure_file("common/config.h.in" "common/config.h")
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
include_directories("${CMAKE_CURRENT_SOURCE_DIR}")
+### cjson library #####
+include(ExternalProject)
+list(APPEND CMAKE_ARGS "-DBUILD_SHARED_LIBS=OFF")
+list(APPEND CMAKE_ARGS "-DENABLE_CUSTOM_COMPILER_FLAGS=OFF")
+list(APPEND CMAKE_ARGS "-DENABLE_CJSON_TEST=OFF")
+ExternalProject_Add(cjson
+ PREFIX ${CMAKE_BINARY_DIR}/cjson
+ GIT_REPOSITORY https://github.com/DaveGamble/cJSON
+ CMAKE_ARGS "${CMAKE_ARGS}"
+ STEP_TARGETS build
+ EXCLUDE_FROM_ALL TRUE
+ )
+include_directories(${CMAKE_BINARY_DIR}/cjson/src)
+
+### Project subdirectories #####
+add_subdirectory(close_im)
add_subdirectory(common)
+add_subdirectory(restapi)
diff --git a/close_im/CMakeLists.txt b/close_im/CMakeLists.txt
index fd5ce8c..aec4090 100644
--- a/close_im/CMakeLists.txt
+++ b/close_im/CMakeLists.txt
@@ -1,7 +1,9 @@
-file(GLOB_RECURSE SOURCES *.cpp)
+file(GLOB_RECURSE SOURCES *.c)
ADD_EXECUTABLE(rocket_close_im ${SOURCES})
-target_link_libraries(rocket_close_im common rest)
-target_link_libraries(rocket_close_im curl)
+target_link_libraries(rocket_close_im common restapi)
+target_link_libraries(rocket_close_im config curl)
+add_dependencies(rocket_close_im cjson-build)
+target_link_libraries(rocket_close_im ${CMAKE_CURRENT_BINARY_DIR}/../cjson/src/cjson-build/libcjson.a)
install(TARGETS rocket_close_im DESTINATION bin)
diff --git a/close_im/main.c b/close_im/main.c
new file mode 100644
index 0000000..b770f3d
--- /dev/null
+++ b/close_im/main.c
@@ -0,0 +1,69 @@
+#include "common/util.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "common/config.h"
+#include "restapi/auth.h"
+#include "restapi/im.h"
+
+int main(void)
+{
+ if (config_load(CONFIG_PATH) != 0) {
+ return 1;
+ }
+
+ char* login = NULL;
+ size_t len = 0;
+ printf("Login: ");
+ ssize_t read = getline(&login, &len, stdin);
+ if (read > 1) login[read-1] = 0;
+
+ struct termios oflags, nflags;
+ tcgetattr(fileno(stdin), &oflags);
+ nflags = oflags;
+ nflags.c_lflag &= ~ECHO;
+ nflags.c_lflag |= ECHONL;
+
+ if (tcsetattr(fileno(stdin), TCSADRAIN, &nflags) != 0) {
+ perror("tcsetattr");
+ return -1;
+ }
+
+ char* password = NULL;
+ printf("Password: ");
+ read = getline(&password, &len, stdin);
+ if (read > 1) password[read-1] = 0;
+
+ if (tcsetattr(fileno(stdin), TCSANOW, &oflags) != 0) {
+ perror("tcsetattr");
+ return -1;
+ }
+
+ if (restapi_login(login, password) == 0) {
+ while(1) {
+ char* buff = NULL;
+ size_t len2;
+ printf("IM to close: ");
+ ssize_t entry = getline(&buff, &len2, stdin);
+ if (entry > 1) {
+ buff[entry-1] = 0;
+ } else {
+ free(buff);
+ break;
+ }
+ restapi_im_close(buff);
+ free(buff);
+ }
+ } else {
+ printf("Couldn't init rest api.\n");
+ }
+
+ restapi_logout();
+ config_clean();
+ free(login);
+ free(password);
+
+ return 0;
+}
diff --git a/close_im/main.cpp b/close_im/main.cpp
deleted file mode 100644
index c412d8b..0000000
--- a/close_im/main.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-#include <cstdlib>
-#include <string>
-#include <unistd.h>
-
-#include "common/config.h"
-#include "rest/rest.hpp"
-#include "common/util.h"
-
-int main(void)
-{
- char* login = NULL;
- size_t len = 0;
- printf("Login: ");
- ssize_t read = getline(&login, &len, stdin);
- if (read > 1) login[read-1] = 0;
-
- char* password = getpass("Password: ");
-
- rest_init(WEB_URL);
-
- if (rest_login(login, password)) {
- while(1) {
- char* buff = NULL;
- size_t len2;
- printf("IM to close: ");
- ssize_t entry = getline(&buff, &len2, stdin);
- if (entry > 1) {
- buff[entry-1] = 0;
- } else {
- free(buff);
- break;
- }
- rest_im_close(buff);
- free(buff);
- }
- } else {
- printf("Couldn't init rest api.\n");
- }
-
- rest_logout();
- rest_clear();
-
- free(login);
-
- return 0;
-}
diff --git a/common/config.c b/common/config.c
index 3320d7c..ff55595 100644
--- a/common/config.c
+++ b/common/config.c
@@ -48,6 +48,24 @@ const char * config_get_wss_url(void)
return url;
}
+const char * config_get_login(void)
+{
+ const char * login;
+ if (config_lookup_string(config, "login", &login) != CONFIG_TRUE) {
+ return NULL;
+ }
+ return login;
+}
+
+const char * config_get_password(void)
+{
+ const char * password;
+ if (config_lookup_string(config, "password", &password) != CONFIG_TRUE) {
+ return NULL;
+ }
+ return password;
+}
+
void config_clean(void)
{
if (config != NULL) {
diff --git a/common/config.h.in b/common/config.h.in
index 9f27b70..4e90ba2 100644
--- a/common/config.h.in
+++ b/common/config.h.in
@@ -12,6 +12,8 @@
char config_load(const char *config_file);
const char * config_get_web_url(void);
const char * config_get_wss_url(void);
+const char * config_get_login(void);
+const char * config_get_password(void);
void config_clean(void);
#endif
diff --git a/common/http.c b/common/http.c
new file mode 100644
index 0000000..275a18f
--- /dev/null
+++ b/common/http.c
@@ -0,0 +1,133 @@
+#include <curl/curl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "http.h"
+#include "util.h"
+
+// Curl internal handling
+CURL* curl = NULL;
+struct curl_slist *headers = NULL;
+
+// Curl buffer handling
+char* buffer = NULL;
+size_t buffer_len = 0;
+size_t buffer_fill = 0;
+
+char initialized = 0;
+
+#define HEADER_SEP ": "
+
+static size_t writeCallback(char* buf, size_t size, size_t nmemb, void* userp);
+const char * http_perform(const char* path, const char* postfields);
+
+void http_add_header(const char* name, const char* value)
+{
+ if (!initialized) {
+ fprintf(stderr, "BUG: http_init wasn't called!\n");
+ return;
+ }
+
+ size_t name_len = strlen(name);
+ size_t sep_len = strlen(HEADER_SEP);
+ size_t value_len = strlen(value);
+ char* header = malloc(name_len + sep_len + value_len + 1);
+ strcpy(header, name);
+ strcpy(header + name_len, HEADER_SEP);
+ strcpy(header + name_len + sep_len, value);
+ headers = curl_slist_append(headers, header);
+ free(header);
+}
+
+void http_clean(void)
+{
+ if (!initialized)
+ return;
+ curl_slist_free_all(headers);
+ curl_easy_cleanup(curl);
+ curl = NULL;
+ curl_global_cleanup();
+ free(buffer);
+}
+
+const char * http_get(const char* path)
+{
+ curl_easy_setopt(curl, CURLOPT_POST, 0);
+ return http_perform(path, NULL);
+}
+
+void http_init(void)
+{
+ if (initialized) {
+ fprintf(stderr, "BUG: http_init already called!\n");
+ return;
+ }
+ curl_global_init(CURL_GLOBAL_ALL);
+ curl = curl_easy_init();
+ initialized = 1;
+}
+
+const char * http_perform(const char* path, const char* postfields)
+{
+ if (!initialized) {
+ fprintf(stderr, "BUG: http_init wasn't called!\n");
+ return NULL;
+ }
+
+ if (buffer != NULL) {
+ memset(buffer, 0, buffer_fill);
+ buffer_fill = 0;
+ }
+
+ curl_easy_reset(curl);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCallback);
+
+ const char * weburl = config_get_web_url();
+ size_t weburl_len = strlen(weburl);
+ size_t path_len = strlen(path);
+ char * url = malloc(weburl_len + path_len + 1);
+ strcpy(url, weburl);
+ strcpy(url + weburl_len, path);
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ free(url);
+ if (postfields != NULL)
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postfields);
+ if (headers != NULL)
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ CURLcode res = curl_easy_perform(curl);
+ if(res != CURLE_OK) {
+ fprintf(stderr, "HTTP get to %s failed: %s\n", url, curl_easy_strerror(res));
+ return NULL;
+ }
+
+ return buffer;
+}
+
+const char * http_post(const char* path, const char* postfields)
+{
+ if (postfields == NULL)
+ curl_easy_setopt(curl, CURLOPT_POST, 1);
+ return http_perform(path, postfields);
+}
+
+static size_t writeCallback(char* buf, size_t size, size_t nmemb, UNUSED void* userp)
+{ //callback must have this declaration
+ //buf is a pointer to the data that curl has for us
+ //size*nmemb is the size of the buffer
+
+ register size_t sn = size * nmemb;
+ if (buffer == NULL) {
+ buffer_len = sn;
+ buffer = malloc (buffer_len + 1);
+ } else if (buffer_fill + sn >= buffer_len) {
+ buffer_len += sn;
+ buffer = realloc(buffer, buffer_len + 1);
+ }
+ for (unsigned int c = 0; c < size * nmemb; ++c) {
+ buffer[buffer_fill + c] = buf[c];
+ }
+ buffer_fill += sn;
+ buffer[buffer_fill] = 0;
+ return sn; //tell curl how many bytes we handled
+}
diff --git a/common/http.h b/common/http.h
new file mode 100644
index 0000000..ed63c9f
--- /dev/null
+++ b/common/http.h
@@ -0,0 +1,10 @@
+#ifndef COMMON_HTTP_H_
+#define COMMON_HTTP_H_
+
+void http_add_header(const char* name, const char* value);
+void http_clean(void);
+const char * http_get(const char* path);
+void http_init(void);
+const char * http_post(const char* path, const char* postfields);
+
+#endif
diff --git a/common/util.h b/common/util.h
index 464e7dc..35970d4 100644
--- a/common/util.h
+++ b/common/util.h
@@ -1,5 +1,5 @@
-#ifndef _UTIL_H_
-#define _UTIL_H_
+#ifndef COMMON_UTIL_H_
+#define COMMON_UTIL_H_
#include <features.h>
#include <time.h>
diff --git a/restapi/CMakeLists.txt b/restapi/CMakeLists.txt
new file mode 100644
index 0000000..45a22c2
--- /dev/null
+++ b/restapi/CMakeLists.txt
@@ -0,0 +1,5 @@
+file(GLOB_RECURSE SOURCES *.c)
+
+ADD_LIBRARY(restapi STATIC ${SOURCES})
+add_dependencies(restapi cjson-build)
+target_link_libraries(restapi common)
diff --git a/restapi/auth.c b/restapi/auth.c
new file mode 100644
index 0000000..e3f1b74
--- /dev/null
+++ b/restapi/auth.c
@@ -0,0 +1,80 @@
+#include <cjson/cJSON.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "auth.h"
+#include "common/config.h"
+#include "common/http.h"
+
+#define USER_STR "user="
+#define PASS_STR "&password="
+
+char // returns 0 if ok, greater than 0 otherwise
+restapi_login(const char* username, const char* password)
+{
+ char ret = 0;
+ http_init();
+ size_t user_str_len = strlen(USER_STR);
+ size_t user_len = strlen(username);
+ size_t pass_str_len = strlen(PASS_STR);
+ size_t pass_len = strlen(password);
+ char* login_args = malloc(user_str_len + user_len + pass_str_len + pass_len + 1);
+ strcpy(login_args, USER_STR);
+ strcpy(login_args + user_str_len, username);
+ strcpy(login_args + user_str_len + user_len, PASS_STR);
+ strcpy(login_args + user_str_len + user_len + pass_str_len, password);
+ const char* buffer = http_post("/api/v1/login", login_args);
+ free(login_args);
+ if (buffer == NULL) {
+ fprintf(stderr, "Error while authenticating, http post didn't return any data.\n");
+ return 1;
+ }
+
+ cJSON* json = cJSON_Parse(buffer);
+ if (json == NULL) {
+ const char *error_ptr = cJSON_GetErrorPtr();
+ if (error_ptr != NULL)
+ fprintf(stderr, "Json parsing error before: %s\n", error_ptr);
+ fprintf(stderr, "Error while authenticating, couldn't parse json output :\n%s\n", buffer);
+ ret = 2;
+ goto login_json_cleanup;
+ }
+
+ const cJSON* status = cJSON_GetObjectItemCaseSensitive(json, "status");
+ if (cJSON_IsString(status) && status->valuestring != NULL && strcmp(status->valuestring, "success") == 0) {
+ cJSON* data = cJSON_GetObjectItemCaseSensitive(json, "data");
+ if (!cJSON_IsObject(data)) {
+ fprintf(stderr, "Error while authenticating, couldn't parse json output :\n%s\n", buffer);
+ ret = 4;
+ goto login_json_cleanup;
+ }
+ cJSON* userId = cJSON_GetObjectItemCaseSensitive(data, "userId");
+ cJSON* authToken = cJSON_GetObjectItemCaseSensitive(data, "authToken");
+ if (!cJSON_IsString(userId) || userId->valuestring == NULL || !cJSON_IsString(authToken) || authToken->valuestring == NULL) {
+ fprintf(stderr, "Error while authenticating, couldn't parse json output :\n%s\n", buffer);
+ ret = 5;
+ goto login_json_cleanup;
+ }
+
+ printf("userid: %s\nauthtoken: %s\n", userId->valuestring, authToken->valuestring);
+ http_add_header("X-User-Id", userId->valuestring);
+ http_add_header("X-Auth-Token", authToken->valuestring);
+ } else {
+ const cJSON* msg = cJSON_GetObjectItemCaseSensitive(json, "message");
+ if (cJSON_IsString(msg) && msg->valuestring != NULL)
+ fprintf(stderr, "Error while authenticating: %s\n", msg->valuestring);
+ else
+ fprintf(stderr, "Error while authenticating.\n%s\n", buffer);
+ ret = 3;
+ }
+login_json_cleanup:
+ cJSON_Delete(json);
+ return ret;
+}
+
+void restapi_logout(void)
+{
+ (void) http_post("/api/v1/logout", NULL);
+ http_clean();
+}
diff --git a/restapi/auth.h b/restapi/auth.h
new file mode 100644
index 0000000..ce5ea10
--- /dev/null
+++ b/restapi/auth.h
@@ -0,0 +1,11 @@
+#ifndef RESTAPI_AUTH_H_
+#define RESTAPI_AUTH_H_
+
+extern char * userId;
+extern char * authToken;
+
+char // returns 0 if ok, greater than 0 otherwise
+restapi_login(const char* username, const char* password);
+void restapi_logout(void);
+
+#endif
diff --git a/restapi/im.c b/restapi/im.c
new file mode 100644
index 0000000..6b9f75c
--- /dev/null
+++ b/restapi/im.c
@@ -0,0 +1,58 @@
+#include <cjson/cJSON.h>
+#include <curl/curl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common/http.h"
+#include "im.h"
+
+#define LOGIN_ARG_PRE "{ \"username\": \""
+#define LOGIN_ARG_POST "\" }"
+
+char // returns 0 if ok, greater than 0 otherwise
+restapi_im_close(const char* username)
+{
+ char ret = 0;
+
+ size_t pre_len = strlen(LOGIN_ARG_PRE);
+ size_t user_len = strlen(username);
+ size_t post_len = strlen(LOGIN_ARG_POST);
+ char* login_args = malloc(pre_len + user_len + post_len + 1);
+ strcpy(login_args, LOGIN_ARG_PRE);
+ strcpy(login_args + pre_len, username);
+ strcpy(login_args + pre_len + user_len, LOGIN_ARG_POST);
+ http_add_header("Content-type", "application/json");
+ const char* buffer = http_post("/api/v1/im.close", login_args);
+ free(login_args);
+
+ if (buffer == NULL) {
+ fprintf(stderr, "Error while im_close, http post didn't return any data.\n");
+ return 1;
+ }
+
+ cJSON* json = cJSON_Parse(buffer);
+ if (json == NULL) {
+ const char *error_ptr = cJSON_GetErrorPtr();
+ if (error_ptr != NULL)
+ fprintf(stderr, "Json parsing error before: %s\n", error_ptr);
+ fprintf(stderr, "Error while im_close, couldn't parse json output :\n%s\n", buffer);
+ ret = 2;
+ goto login_json_cleanup;
+ }
+
+ const cJSON* status = cJSON_GetObjectItemCaseSensitive(json, "status");
+ if (cJSON_IsString(status) && status->valuestring != NULL && strcmp(status->valuestring, "success") == 0) {
+ printf("user %s removed\n", username);
+ } else {
+ const cJSON* msg = cJSON_GetObjectItemCaseSensitive(json, "message");
+ if (cJSON_IsString(msg) && msg->valuestring != NULL)
+ fprintf(stderr, "Error while im_close: %s\n", msg->valuestring);
+ else
+ fprintf(stderr, "Error while im_close.\n%s\n", buffer);
+ ret = 3;
+ }
+login_json_cleanup:
+ cJSON_Delete(json);
+ return ret;
+}
diff --git a/restapi/im.h b/restapi/im.h
new file mode 100644
index 0000000..fe85860
--- /dev/null
+++ b/restapi/im.h
@@ -0,0 +1,7 @@
+#ifndef RESTAPI_IM_H_
+#define RESTAPI_IM_H_
+
+char // returns 0 if ok, greater than 0 otherwise
+restapi_im_close(const char* username);
+
+#endif