qlic

Zoho Cliq but not really

commit dfa6876fe79a49cbd2faaa2fb7250e346057d57f
parent 3014f4b315e5e0c40c015499b2e80e1728e1fd31
Author: Bharatvaj <bharatvaj@yahoo.com>
Date: Wed, 20 Apr 2022 23:30:56 +0530

Add qlic_oauth.h

Add config.h for storing compile time values

Handle basic argument parsing for testing
12 files changed, 170 insertions(+), 27 deletions(-)
M
Makefile
|
4
++--
M
cliq_apis.c
|
9
++++-----
A
config.h
|
11
+++++++++++
A
config.json
|
6
++++++
M
qlic.c
|
37
+++++++++++++++++++++++++++----------
M
qlic_common.c
|
12
++++++++----
M
qlic_common.h
|
2
+-
A
qlic_oauth.h
|
91
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M
qlic_response_handler.c
|
13
++++++++++---
M
qlic_response_handler.h
|
2
+-
M
qlic_types.h
|
2
+-
A
state.json
|
8
++++++++
diff --git a/Makefile b/Makefile
@@ -1,5 +1,5 @@
-qlic: qlic.c
-	$(CC) qlic.c qlic_response_handler.c qlic_common.c cliq_apis.c -o qlic -I. -lcurl -g
+qlic: qlic.c qlic_response_handler.* qlic_common.* cliq_apis.* qlic_oauth.h
+	$(CC) qlic.c qlic_response_handler.c qlic_common.c cliq_apis.c -o qlic -I. -I${HOME}/sysroot/include -L${HOME}/sysroot/lib -lnxjson -loauth -lcurl -g
 
 test: qlic
 	./qlic -r
diff --git a/cliq_apis.c b/cliq_apis.c
@@ -3,13 +3,12 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#define __QLIC_SEND_MESSAGE_STR "https://cliq.zoho.com/api/v2/chats/%s/message"
 QlicString* qlic_send_message_str(QlicString* chat_id) {
+#define __QLIC_SEND_MESSAGE_API_STR "https://cliq.zoho.com/api/v2/chats/%s/message"
 	QlicString* send_message = init_qlic_string();
-	// tenet moment
 	// minus 2 for removing the format specifier :(
-	send_message->len = sizeof(__QLIC_SEND_MESSAGE_STR) + chat_id->len - 2;
-	send_message->string = (char*)malloc(send_message->len);
-	snprintf(send_message->string, send_message->len, __QLIC_SEND_MESSAGE_STR, chat_id->string);
+	send_message->len = sizeof(__QLIC_SEND_MESSAGE_API_STR) + chat_id->len - 2;
+	send_message->string = (char *)malloc(send_message->len * sizeof(char));
+	snprintf(send_message->string, send_message->len, __QLIC_SEND_MESSAGE_API_STR, chat_id->string);
 	return send_message;
 }
diff --git a/config.h b/config.h
@@ -0,0 +1,11 @@
+#ifndef __CLIQ_CONFIG_H
+#define __CLIQ_CONFIG_H
+#define CLIQ_AUTH_ENDPOINT "https://accounts.zoho.com/oauth/v2/auth"
+#define CLIQ_TOKEN_ENDPOINT "https://accounts.zoho.com/oauth/v2/token"
+
+#define CLIQ_CLIENT_ID ""
+#define CLIQ_CLIENT_SECRET ""
+#define CLIQ_REDIRECT_URI "https://127.0.0.1:8443/hello"
+#define CLIQ_SCOPE "ZohoCliq.Chats.READ,ZohoCliq.Messages.READ,ZohoCliq.Webhooks.CREATE"
+
+#endif
diff --git a/config.json b/config.json
@@ -0,0 +1,6 @@
+{
+	"user_id": {
+		"client_id": "",
+		"client_secret": ""
+	}
+}
diff --git a/qlic.c b/qlic.c
@@ -4,27 +4,44 @@
 #include <qlic_common.h>
 #include <cliq_apis.h>
 #include <qlic_response_handler.h>
+#include <qlic_oauth.h>
 
+int qlic_send_text_msg(const char* __access_token, const char* __chat_id) {
+	QlicString* access_token = NULL;
+	access_token = init_qlic_string();
+	access_token->len = strlen(__access_token);
+	access_token->string = (char*)malloc(access_token->len * sizeof(__access_token));
+	strncpy(access_token->string, __access_token, access_token->len);
+	QlicContext* qlic_context = qlic_context_access_init(access_token);
+	if (qlic_context == NULL) {
+		qlic_error("Cannot init network library");
+		return -1;
+	}
+	QlicString* chat_id = init_qlic_string();
+	__QLIC_ASSIGN_STRING(chat_id, __chat_id);
+	qlic_context->request_url = qlic_send_message_str(chat_id);
+	qlic_request(qlic_context, qlic_handle_send_message, true);
+	return 0;
+}
 
 // TODO Send error back
-#define __QLIC_ACCESS_TOKEN "Zoho-oauthtoken "
 int main(int argc, char* argv[]) {
-	QlicString* access_token = init_qlic_string();
-	__QLIC_ASSIGN_STRING(access_token, __QLIC_ACCESS_TOKEN);
 	if (argc == 1) {
 		qlic_error("Not enough arguments");
 		return -1;
 	}
+	// TODO Use an argument parsing library
 	if (strcmp(argv[1], "-r") == 0) {
-		QlicContext* qlic_context = qlic_context_access_init(access_token);
-		if (qlic_context == NULL) {
-			qlic_error("Cannot init network library");
+		// TODO read access_token from state.json
+		char* access_token = "1000.429cf5132d6cc978960bfdd6e0a425cc.80bbd5584b0c35133c4f82143e6811b2";
+		// FIXME possible buffer overflow here
+		qlic_send_text_msg(access_token, argv[2]);
+	} else if (strcmp(argv[1], "-a") == 0) {
+		char* access_token = start_oauth_server();
+		if (access_token == NULL) {
+			qlic_error("Access token is empty, authentication failed");
 			return -1;
 		}
-		QlicString* chat_id = init_qlic_string();
-		__QLIC_ASSIGN_STRING(chat_id, "2243227993181997558");
-		qlic_context->request_url = qlic_send_message_str(chat_id);
-		qlic_request(qlic_context, qlic_handle_send_message, true);
 	}
 	return 0;
 }
diff --git a/qlic_common.c b/qlic_common.c
@@ -2,6 +2,7 @@
 #include <curl/curl.h>
 #include <stdlib.h>
 #include <string.h>
+#include <qlic_response_handler.h>
 
 void qlic_error(const char* error_message) {
 	fprintf(stderr, error_message);

@@ -13,8 +14,8 @@ static struct curl_slist* __qlic_set_request_headers(QlicContext* context, QlicS
 		return NULL;
 	}
 	struct curl_slist* list = NULL;
-#define __QLIC_AUTHORIZATION_HEADER "Authorization: "
-	// TODO cleanup authorization_header if curl doesn't handle it?
+#define __QLIC_AUTHORIZATION_HEADER "Authorization: Zoho-oauthtoken "
+	// TODO free up authorization_header if curl doesn't handle
 	size_t authorization_header_len = sizeof(__QLIC_AUTHORIZATION_HEADER) + access_token->len;
 	char* authorization_header = (char*)malloc(authorization_header_len);
 	strncpy(authorization_header, __QLIC_AUTHORIZATION_HEADER, sizeof(__QLIC_AUTHORIZATION_HEADER));

@@ -67,12 +68,15 @@ void qlic_request(QlicContext* context, qlic_response_callback callback, bool is
 		CURL* curl = (CURL*)context->context;
 		CURLcode res;
 		curl_easy_setopt(curl, CURLOPT_URL, context->request_url->string);
-		/* curl_easy_setopt(curl, CURLOPT_GET, 1); */
 		/* curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); */
-		curl_easy_setopt(curl, CURLOPT_READFUNCTION, callback);
 		if (is_post_request) {
 			curl_easy_setopt(curl, CURLOPT_POST, 1);
+			curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"text\": \"Hi\"}");
 		}
+		/* curl_easy_setopt(curl, CURLOPT_READFUNCTION, qlic_handle_read_chat); */
+		/* curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); */
+		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);
+		curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl);
 		res = curl_easy_perform(curl);
 		if(res != CURLE_OK) {
 			fprintf(stderr, "curl_easy_perform() failed: %s\n",
diff --git a/qlic_common.h b/qlic_common.h
@@ -6,7 +6,7 @@
 
 #define __QLIC_ASSIGN_STRING(X,Y) \
 	X->string = Y; \
-	X->len = sizeof(Y);
+	X->len = strlen(Y);
 
 void qlic_error(const char* error_message);
 
diff --git a/qlic_oauth.h b/qlic_oauth.h
@@ -0,0 +1,91 @@
+#ifndef __QLIC_OAUTH_H
+#define __QLIC_OAUTH_H
+
+#include <oauth2.h>
+#include <stdio.h>
+#include <config.h>
+#include <nxjson.h>
+
+// TODO Choose between DB and text files for saving this information
+// If using text, choose between formats, yaml or json or other format, which is more suckless
+// If DB, sliqte3 is a good choice, but don't
+// Going to need a json parser and writer, might as well make the config files as json as well
+const char* saved_grant_token = NULL;
+const char* saved_access_token = NULL;
+/* const char* saved_grant_token = ""; */
+
+/* const char* saved_access_token = ""; */
+
+// TODO rethink this decision later, char* or const char* ?
+// Have to free the returning string at the end, who does that?
+char *json_access_code_transformer(char* str) {
+	const nx_json* json = nx_json_parse(str, nx_json_unicode_to_utf8);
+	if (json->type == NX_JSON_OBJECT) {
+		const nx_json* at = nx_json_get(json, "access_token");
+		if (at->type == NX_JSON_STRING) {
+			printf("str: %s\n", str);
+			const char* value = at->text_value;
+			size_t len = strlen(value);
+			char* copy = (char*)malloc(len);
+			strncpy(copy, value, len);
+			printf("copy: %s\n", copy);
+			return copy;
+		}
+	}
+	return NULL;
+}
+
+char* start_oauth_server() {
+	oauth2_config* conf = oauth2_init(CLIQ_CLIENT_ID, CLIQ_CLIENT_SECRET);
+	conf->access_auth_code_transformer = json_access_code_transformer;
+    oauth2_set_redirect_uri(conf, CLIQ_REDIRECT_URI);
+	// TODO generate true state instead of LOL
+	char* redir_uri = NULL;
+	char* grant_token = malloc(255 * sizeof(char));
+	if (saved_grant_token == NULL) {
+		redir_uri = oauth2_request_auth_code(conf, CLIQ_AUTH_ENDPOINT, CLIQ_SCOPE, "LOL", "online");
+
+		printf("Visit this url and hit authorize: %s\n", redir_uri);
+		printf("Now put the auth token here: ");
+
+		scanf("%s", grant_token);
+	} else {
+		strcpy(grant_token, saved_grant_token);
+	}
+
+	if (grant_token == NULL) {
+		printf("grant_token is null");
+		return NULL;
+	}
+
+    //Now test token based auth
+    char* access_token = NULL;
+	if (saved_access_token == NULL) {
+		access_token = oauth2_access_auth_code(conf, CLIQ_TOKEN_ENDPOINT, grant_token, CLIQ_SCOPE);
+		if (access_token == NULL) {
+			printf("access_token: %ld is null\n", (long)access_token);
+			return NULL;
+		}
+	} else {
+		access_token = malloc(255 * sizeof(char));
+		strcpy(access_token, saved_access_token);
+	}
+
+    oauth2_set_auth_code(conf, access_token);
+    printf("Access Token: %s\n", access_token);
+    /* free(access_token); */
+
+    /* printf("Enter your Facebook status: "); */
+    /* char status[255]; */
+    /* scanf("%s", status); */
+    /* char status2[255]; */
+    /* sprintf(status2, "message=%s", status); */
+
+    /* access_token = oauth2_request(conf, "https://graph.facebook.com/slugonamission/feed", status2); */
+
+    /* printf("%s\n", access_token); */
+
+    oauth2_cleanup(conf);
+	return access_token;
+}
+#endif
diff --git a/qlic_response_handler.c b/qlic_response_handler.c
@@ -1,12 +1,19 @@
 #include <qlic_response_handler.h>
 #include <stdio.h>
+#include <string.h>
 
-int qlic_handle_read_chat(char* response, size_t response_size, size_t nmemb, void *userp) {
+int qlic_handle_read_chat(char* response, size_t item_size, size_t nmemb, void *userp) {
 	/* printf("helo: %s\n", response); */
+	/* size_t bytes = item_size * nmemb; */
+/* #define QLIC_TEST_TEST "{\"text\": \"Hi\"}" */
+/* 	printf("Test: item_size %zu", item_size); */
+/* 	printf("Test: response %s", response); */
 	return 0;
 }
 
-int qlic_handle_send_message(char* response, size_t response_size, size_t nmemb, void *userp) {
+size_t qlic_handle_send_message(char* response, size_t item_size, size_t nmemb, void *userp) {
+	size_t bytes = item_size * nmemb;
+	printf("Chunk size: %zu\n", nmemb);
 	printf("%s\n", response);
-	return 0;
+	return bytes;
 }
diff --git a/qlic_response_handler.h b/qlic_response_handler.h
@@ -4,6 +4,6 @@
 #include <stddef.h>
 
 int qlic_handle_read_chat(char* response, size_t response_size, size_t nmemb, void *userp);
-int qlic_handle_send_message(char* response, size_t response_size, size_t nmemb, void *userp);
+size_t qlic_handle_send_message(char* response, size_t response_size, size_t nmemb, void *userp);
 
 #endif
diff --git a/qlic_types.h b/qlic_types.h
@@ -3,7 +3,7 @@
 
 #include <stddef.h>
 
-typedef int (*qlic_response_callback)(char*, size_t, size_t, void*);
+typedef size_t (*qlic_response_callback)(char*, size_t, size_t, void*);
 
 typedef struct QlicString {
 	char* string;
diff --git a/state.json b/state.json
@@ -0,0 +1,8 @@
+{
+	"user_id": {
+		"grant_token": "",
+		"access_token": "",
+		"refresh_token": "",
+		"time_generated": ""
+	}
+}