commit ad83f361880eccefef1dd0cc9e08cf18bb61c185
parent dfa6876fe79a49cbd2faaa2fb7250e346057d57f
Author: Bharatvaj <bharatvaj@yahoo.com>
Date: Thu, 9 Jun 2022 05:11:23 +0530
Parse local config.json with nxjson
Add options '-v' for getting version
Remove defines fields in config.h
Move all defines to qlic_private.h
Diffstat:
10 files changed, 224 insertions(+), 84 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,2 +1,3 @@
.vimrc
qlic
+qlic.dSYM/
diff --git a/README b/README
@@ -1,59 +0,0 @@
-qlic
-====
-
-qlic is a cli frontend to the venereal Zoho cliq. It is cliq but unopinionated.
-
-NOTE: Built with bubble gum and paper clip. Highly unstable, please use at your own risk.
-
-![cliq-jpeg](images/qlic.jpg)
-
-Why?
-----
-I avoid browsers like the plague. Zoho cliq was my only reason for using a browser. Not anymore.
-
-What works
-----------
-* Authentication via OAuth
-* Receive and send text to chat, channels
-* Receive and send attachments to chat, channels
-
-What doesn't work
------------------
-* Cliq call - Apparently this takes more work than I thought.
-* Screenshare - Sharing screen is top priority for qlic, but it requires call to work first.
-* Widgets - I have no interest in implementing widgets in a cli as it doesn't benefit me, but contributions are welcome as always.
-
-Unique features
----------------
-* stdin support!
- - You can send attachment to qlic's `stdin`
-```sh
-# NOTE: `nwoo` is the recipient
-# qlic tries to match with the closest name, if it matches with multiple names, a prompt will be shown to the user
-
-# Sending a text
-echo "hello fren" | qlic -r nwoo
-qlic -r nwooo "henlo fren"
-
-# Sending an attachment
-cat how-to-basic-101.jpg | qlic -r nwoo -b meme.jpg
-```
-
-Install
--------
-```sh
-git clone https://github.com/bharatvaj/qlic
-cd qlic
-make install
-```
-
-Setup
------
-* Create a config file at `$XDG_DATA_HOME/qlic/data.json` with the following configuration
-```json
-{
- client_id: 'your_client_id',
- client_secret: 'your_client_secret',
-}
-```
-NOTE: client id and secret can be obtained from [Zoho's API console](https://api-console.zoho.com/)
diff --git a/README.rst b/README.rst
@@ -0,0 +1,65 @@
+qlic
+====
+
+qlic is an unecessary fast cli frontend to the venereal Zoho cliq. It is cliq but unopinionated.
+
+NOTE: Built with bubble gum and paper clip. Highly unstable, please use at your own risk.
+
+![cliq-jpeg](images/qlic.jpg)
+
+Why?
+----
+I avoid browsers like the plague. Zoho cliq was my only reason for using a browser. Not anymore.
+
+What works
+----------
+* Authentication via OAuth
+* Receive and send text to chat, channels
+* Receive and send attachments to chat, channels
+
+What doesn't work
+-----------------
+* Cliq call - Apparently this takes more work than I thought.
+* Screenshare - Sharing screen is top priority for qlic, but it requires call to work first.
+* Widgets - I have no interest in implementing widgets in a cli as it doesn't benefit me, but contributions are welcome as always.
+
+Unique features
+---------------
+* stdin support!
+ - You can send attachment to qlic's `stdin`
+```sh
+# NOTE: `nwoo` is the recipient
+# qlic tries to match with the closest name, if it matches with multiple names, a prompt will be shown to the user
+
+# Sending a text
+echo "hello fren" | qlic -r nwoo
+qlic -r nwooo "henlo fren"
+
+# Sending an attachment
+cat how-to-basic-101.jpg | qlic -r nwoo -b meme.jpg
+```
+
+Install
+-------
+```sh
+git clone https://github.com/bharatvaj/qlic
+cd qlic
+make install
+```
+
+Dependencies
+------------
+* libcurl
+* bharatvaj/OAuth2
+* yarosla/nxjson
+
+Setup
+-----
+* Create a config file at `$XDG_DATA_HOME/qlic/data.json` with the following configuration
+```json
+{
+ client_id: 'your_client_id',
+ client_secret: 'your_client_secret',
+}
+```
+NOTE: client id and secret can be obtained from [Zoho's API console](https://api-console.zoho.com/)
diff --git a/config.h b/config.h
@@ -1,10 +1,12 @@
#ifndef __CLIQ_CONFIG_H
#define __CLIQ_CONFIG_H
+
+#define QLIC_VERSION "0.0.1"
+
#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"
diff --git a/qlic.c b/qlic.c
@@ -5,6 +5,8 @@
#include <cliq_apis.h>
#include <qlic_response_handler.h>
#include <qlic_oauth.h>
+#include <qlic_private.h>
+#include "config.h"
int qlic_send_text_msg(const char* __access_token, const char* __chat_id) {
QlicString* access_token = NULL;
@@ -24,23 +26,32 @@ int qlic_send_text_msg(const char* __access_token, const char* __chat_id) {
return 0;
}
+static void qlic_usage() {
+ fputs("usage: qlic [-va] [-r chat_id]\n", stderr);
+ exit(1);
+}
+
// TODO Send error back
int main(int argc, char* argv[]) {
- if (argc == 1) {
- qlic_error("Not enough arguments");
- return -1;
- }
- // TODO Use an argument parsing library
- if (strcmp(argv[1], "-r") == 0) {
- // 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;
+ int i;
+ QlicContext* ctx = qlic_init();
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-v")) {
+ fputs("qlic"QLIC_VERSION"\n", stderr);
+ } else if (!strcmp(argv[i], "-a")) {
+ char* access_token = start_oauth_server(ctx);
+ if (access_token == NULL) {
+ qlic_error("Access token is empty, authentication failed");
+ return -1;
+ }
+ /* these options take one argument */
+ } else if (!strcmp(argv[i], "-r")) {
+ // 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[++i]);
+ } else {
+ qlic_usage();
}
}
return 0;
diff --git a/qlic_common.c b/qlic_common.c
@@ -5,9 +5,11 @@
#include <qlic_response_handler.h>
void qlic_error(const char* error_message) {
- fprintf(stderr, error_message);
+ fprintf(stderr, "%s\n", error_message);
}
+
+
static struct curl_slist* __qlic_set_request_headers(QlicContext* context, QlicString* access_token) {
CURL* curl = (CURL*)context->context;
if(access_token == NULL) {
@@ -55,7 +57,7 @@ void destroy_qlic_string(QlicString* qlic_string) {
QlicContext* qlic_init() {
CURL *curl = curl_easy_init();
if (curl) {
- QlicContext* qlic_context = (QlicContext*)malloc(sizeof(QlicContext));
+ QlicContext* qlic_context = (QlicContext*)calloc(1, sizeof(QlicContext));
qlic_context->context = curl;
return qlic_context;
} else {
diff --git a/qlic_common.h b/qlic_common.h
@@ -4,10 +4,6 @@
#include <qlic_types.h>
#include <stdbool.h>
-#define __QLIC_ASSIGN_STRING(X,Y) \
- X->string = Y; \
- X->len = strlen(Y);
-
void qlic_error(const char* error_message);
QlicString* init_qlic_string();
diff --git a/qlic_oauth.h b/qlic_oauth.h
@@ -6,6 +6,9 @@
#include <config.h>
#include <nxjson.h>
+#include "qlic_types.h"
+#include "qlic_private.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
@@ -35,8 +38,93 @@ char *json_access_code_transformer(char* str) {
return NULL;
}
-char* start_oauth_server() {
- oauth2_config* conf = oauth2_init(CLIQ_CLIENT_ID, CLIQ_CLIENT_SECRET);
+
+/*
+ * returns >1 if the file is sucessfully read
+ * returns the err value (<=0) in case of errors
+ * return -2 if dest is NULL
+ * returs -3 if dest_len is NULL
+ */
+uint8_t read_contents(char* dest, size_t* dest_len, FILE* fp) {
+ if (dest == NULL) {
+ return -2;
+ }
+ if (dest_len == NULL) {
+ return -3;
+ }
+ int err = -1;
+ size_t res = *dest_len;
+ while ((res = fread(dest, 1, QLIC_FILE_BUFFER_SIZE, fp)) > 0) {
+ *dest_len += res;
+ if (res == 0) {
+ if((err = ferror(fp)) != 0) {
+ // error occured
+ return err;
+ } else if((err = feof(fp)) != 0) {
+ // return okay
+ return err;
+ }
+ /// unknown error
+ return -1;
+ } else {
+ // res > QLIC_FILE_READ_SIZE
+ dest = realloc(dest, *dest_len + QLIC_FILE_BUFFER_SIZE);
+ }
+ }
+ return -1;
+}
+
+QlicErrorCode qlic_read_config_file(QlicContext* ctx) {
+ /// @todo use xdg paths
+ FILE* fp = fopen("config.json", "r");
+ char* dest = malloc(sizeof(char) * QLIC_FILE_BUFFER_SIZE);
+ size_t dest_len = QLIC_FILE_BUFFER_SIZE;
+ read_contents(dest, &dest_len, fp);
+ const nx_json* json = nx_json_parse(dest, nx_json_unicode_to_utf8);
+ if (json->type == NX_JSON_OBJECT) {
+ const nx_json* at_user = nx_json_get(json, "user_id");
+ if (at_user == NULL) return QLIC_ERROR;
+ if (at_user->type == NX_JSON_OBJECT) {
+ const nx_json* at_client_id = nx_json_get(at_user, "client_id");
+ if (at_client_id == NULL) return QLIC_ERROR;
+ if (at_client_id->type == NX_JSON_STRING) {
+ qlic_error(at_client_id->text_value);
+ /// @todo maybe nxjson already string length has it?
+ __QLIC_ASSIGN_STRING(ctx->client_id, at_client_id->text_value);
+ qlic_error(ctx->client_id->string);
+ if (at_client_id->text_value == NULL) {
+ return QLIC_ERROR;
+ }
+ }
+ const nx_json* at_client_secret = nx_json_get(at_user, "client_secret");
+ if (at_client_secret == NULL) return QLIC_ERROR;
+ if (at_client_secret->type == NX_JSON_STRING) {
+ qlic_error(at_client_secret->text_value);
+ /// @todo maybe nxjson already string length has it?
+ __QLIC_ASSIGN_STRING(ctx->client_secret, at_client_secret->text_value);
+ qlic_error(ctx->client_secret->string);
+ if (at_client_secret->text_value == NULL) {
+ return QLIC_ERROR;
+ }
+ }
+ }
+ }
+ return QLIC_ERROR;
+}
+
+char* start_oauth_server(QlicContext* ctx) {
+ int err = -1;
+ err = qlic_read_config_file(ctx);
+ if (err) {
+ qlic_error("Not able to read config file");
+ QLIC_PANIC();
+ }
+
+ oauth2_config* conf = oauth2_init(ctx->client_id->string, ctx->client_secret->string);
+ if (conf == NULL) {
+ qlic_error("conf is null\n");
+ QLIC_PANIC();
+ }
conf->access_auth_code_transformer = json_access_code_transformer;
oauth2_set_redirect_uri(conf, CLIQ_REDIRECT_URI);
// TODO generate true state instead of LOL
diff --git a/qlic_private.h b/qlic_private.h
@@ -0,0 +1,25 @@
+#ifndef __QLIC_PRIVATE_H
+#define __QLIC_PRIVATE_H
+
+#define QLIC_FILE_BUFFER_SIZE 256
+/// @todo this can be optimized based on the cpu
+
+#define QLIC_PANIC() \
+ fprintf(stderr, "qlick panicked at %s:%d", __FILE__, __LINE__); \
+ exit(-1);
+
+#define __QLIC_ASSIGN_STRING(X,Y) \
+ if (X == NULL) { \
+ if ((X = init_qlic_string()) == NULL) { \
+ QLIC_PANIC(); \
+ } \
+ } \
+ X->len = strlen(Y); \
+ X->string = (char*)malloc(sizeof(char) * (X->len + 2)); \
+ void* __LINE__ptr = strncpy(X->string, Y, X->len); \
+ if (__LINE__ptr == NULL) { \
+ qlic_error("%d in %s failed\n __LINE__ - 2, __FILE__"); \
+ QLIC_PANIC(); \
+ }
+
+#endif
diff --git a/qlic_types.h b/qlic_types.h
@@ -3,6 +3,8 @@
#include <stddef.h>
+#include <qlic_private.h>
+
typedef size_t (*qlic_response_callback)(char*, size_t, size_t, void*);
typedef struct QlicString {
@@ -10,6 +12,11 @@ typedef struct QlicString {
size_t len;
} QlicString;
+typedef enum QlicErrorCode {
+ QLIC_ERROR = 0,
+ QLIC_OK
+} QlicErrorCode;
+
struct QlicCliqAction {
char* request_url;
size_t request_url_len;
@@ -22,6 +29,8 @@ struct QlicCliqAction {
typedef struct QlicContext {
void* context;
QlicString* request_url;
+ QlicString* client_id;
+ QlicString* client_secret;
} QlicContext;
#endif