From 4b27c1a348d8de036dabd5525452f5b9ad08a32b Mon Sep 17 00:00:00 2001 From: nirav Date: Thu, 21 Mar 2019 16:34:24 +0530 Subject: Use g_threads, add builder ui for timeline --- Makefile | 3 +- data/post.ui | 238 ++++++++++++++++++++++++++++++++++ src/auth.c | 347 ++++++++++++-------------------------------------- src/auth.h | 7 +- src/config.c | 46 ++++++- src/config.h | 8 ++ src/http.c | 14 +- src/log.c | 4 +- src/login_window.c | 56 ++++---- src/main.c | 3 +- src/register.c | 128 +++++++++++++++++++ src/register.h | 8 ++ src/string-util.c | 38 ++++++ src/string-util.h | 1 + src/timeline.c | 27 +++- src/timeline_window.c | 81 ++++++++++-- 16 files changed, 674 insertions(+), 335 deletions(-) create mode 100644 data/post.ui create mode 100644 src/register.c create mode 100644 src/register.h diff --git a/Makefile b/Makefile index d214f26..0134d5e 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ CC=cc PKGCONFIG=$(shell which pkg-config) CFLAGS=-o2 -pipe -CFLAGS+=-Wall -Wextra -Wno-unused-parameter +CFLAGS+=-Wall -Wextra -Wno-unused-parameter -std=c99 CFLAGS+=$(shell $(PKGCONFIG) --cflags gtk+-3.0 libcurl jansson) -lpthread LDFLAGS+=$(shell $(PKGCONFIG) --libs gtk+-3.0 libcurl jansson) -lpthread DESTDIR= @@ -17,6 +17,7 @@ OBJECTS=$(OUTDIR)/main.o \ $(OUTDIR)/status.o \ $(OUTDIR)/timeline.o \ $(OUTDIR)/auth.o \ + $(OUTDIR)/register.o \ $(OUTDIR)/login_window.o \ $(OUTDIR)/timeline_window.o \ $(OUTDIR)/config.o \ diff --git a/data/post.ui b/data/post.ui new file mode 100644 index 0000000..da222d8 --- /dev/null +++ b/data/post.ui @@ -0,0 +1,238 @@ + + + + + + + True + False + 4 + 4 + 4 + 4 + 8 + top + + + True + False + start + gtk-select-color + 6 + + + False + False + 0 + + + + + True + False + start + vertical + 8 + + + True + False + + + True + False + Display Name + + + False + True + 0 + + + + + True + False + @username + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + A widget’s index in the overlay children list determines which order the children are drawn if they overlap. The first child is drawn at the bottom. It also affects the default focus chain order. + True + True + char + + + False + True + 1 + + + + + True + False + 20 + + + True + False + start + 10 + 5 + + + True + False + mail-reply-sender + + + False + True + 0 + + + + + True + False + 4 + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + start + 10 + 5 + + + True + False + media-playlist-repeat + + + False + True + 0 + + + + + True + False + 4 + + + False + True + 1 + + + + + False + True + 1 + + + + + True + False + 10 + 5 + + + True + False + emblem-favorite + + + False + True + 0 + + + + + True + False + 4 + + + False + True + 1 + + + + + False + True + 2 + + + + + False + True + 2 + + + + + False + True + 1 + + + + diff --git a/src/auth.c b/src/auth.c index 3a2ee17..c903e67 100644 --- a/src/auth.c +++ b/src/auth.c @@ -10,328 +10,137 @@ #include "config.h" #include "log.h" -#define CLIENT_NAME "ap_client" -#define PROTOCOL_PREFIX "https://" +#define LOGIN_URL "https://%s/oauth/token" -static const char *app_register_url = "/api/v1/apps"; -static const char *login_url = "/oauth/token"; - -int read_local_credentials() -{ - if (!(config->access_token) || !*(config->access_token) || - !(config->instance_url) || !*(config->instance_url)) { - log_msg(LOG_WARNING, "read_local_credentials", - "access_token not found"); - return -1; - } - printf("token: %s\niu: %s\n", config->access_token, config->instance_url); - return 0; -} +struct login_response { + char *access_token; + char *scope; +}; -bool is_logged_in() +static char *get_login_url(const char *domain) { - if (config->access_token) - return true; - return false; -} + char *url; + size_t size; -const char *get_access_token() -{ - return (const char *)config->access_token; -} + size = strlen(LOGIN_URL) + strlen(domain) - 1; + url = malloc(size); + if (!url) { + err(1, NULL); + return NULL; + } -const char *get_instance_url() -{ - return (const char *)config->instance_url; + sprintf(url, LOGIN_URL, domain); + return url; } -struct register_call_arg { - char *url; - char *domain; - char *post_data; - void (*callback)(bool); -}; - -static void *register_call(void *req_arg) +static char *get_login_req(const char *email, const char *password) { - struct register_call_arg *arg; - char *resp; - json_t *root; + char *req; + json_t *json_root; + json_error_t error; - arg = (struct register_call_arg *)req_arg; - if (!arg || !(arg->url)) { - log_msg(LOG_WARNING, "register_call", "invalid arguments"); - goto error; + json_root = json_pack_ex(&error, 1, "{s:s, s:s, s:s, s:s, s:s}", + "client_id", config->client_id, "client_secret", + config->client_secret, "grant_type", "password", "username", email, + "password", password); + if (!json_root) { + return NULL; } - resp = post_request(arg->url, arg->post_data); - if (!resp) { - log_msg(LOG_WARNING, "register_call", "failed to send http request"); - goto error; - } + req = json_dumps(json_root, 0); + json_decref(json_root); - if (!resp) { - log_msg(LOG_WARNING, "register_call", "null response"); - goto error; - } + return req; +} - root = json_loads(resp, 0, NULL); - if (!root) { - log_msg(LOG_WARNING, "register_call", "failed to parse json"); - goto error; - } +static struct login_response *get_login_resp(char *data) +{ + struct login_response *resp; + json_t *json_root; + json_t *access_token, *scope; - if (!json_is_object(root)) { - log_msg(LOG_WARNING, "register_call", "json root is not object"); - goto error; + json_root = json_loads(data, 0, NULL); + if (!json_root) { + return NULL; } - json_t *cid = json_object_get(root, "client_id"); - json_t *csec = json_object_get(root, "client_secret"); - if (!json_is_string(cid) || !json_is_string(csec)) { - log_msg(LOG_WARNING, "register_call", - "invalid client_id or client_secret"); - goto error; + if (!json_is_object(json_root)) { + json_decref(json_root); + return NULL; } - config_set_client_id(json_string_value(cid)); - config_set_client_secret(json_string_value(csec)); - config_set_instance_url(arg->domain); - if (!*(config->client_id) || !*(config->client_secret)) { - log_msg(LOG_WARNING, "register_call", - "invalid client_id or client_secret"); - goto error; + access_token = json_object_get(json_root, "access_token"); + scope = json_object_get(json_root, "scope"); + if (!json_is_string(access_token) || !json_is_string(scope)) { + json_decref(json_root); + return NULL; } - free(arg->url); - if (arg->post_data) - free(arg->post_data); - free(arg); - free(resp); - json_decref(root); - (*(arg->callback))(true); - return NULL; + resp = calloc(1, sizeof(struct login_response)); + resp->access_token = strdup(json_string_value(access_token)); + resp->scope = strdup(json_string_value(scope)); -error: - if (arg) { - if (arg->url) - free(arg->url); - if (arg->post_data) - free(arg->post_data); - free(arg); - } - if (resp) - free(resp); - if (root) - json_decref(root); - (*(arg->callback))(false); - return NULL; + json_decref(json_root); + return resp; } -int register_app(const char *instance, void (*callback)(bool success)) +int login(const char *email, const char *password) { - size_t size; - char *instance_prefix; - char *url, *req_data; - struct register_call_arg *arg; - json_t *root; - json_error_t error; - pthread_t t; + char *url, *req_data, *resp_data; + struct login_response *resp; - if (!instance || strlen(instance) < 1 || !callback) { - log_msg(LOG_WARNING, "register_app", "invalid argument"); + if (!email || !*email || !password || !*password) { + log_msg(LOG_WARNING, "login", "invalid argument"); return -1; } - size = sizeof(PROTOCOL_PREFIX) + sizeof(instance) + 1; - instance_prefix = malloc(size); - if (!instance_prefix) { - err(1, NULL); + if (!is_registered()) { + log_msg(LOG_WARNING, "login", "app not registred"); return -1; } - strlcpy(instance_prefix, PROTOCOL_PREFIX, size); - strlcat(instance_prefix, instance, size); - - root = json_pack_ex(&error, 1, "{s:s, s:s, s:s}", "client_name", - CLIENT_NAME, "redirect_uris", "urn:ietf:wg:oauth:2.0:oob", "scopes", - "read write push"); - if (!root) { - log_msg(LOG_WARNING, "register_app", "json pack error: line %d: %s", - error.line, error.text); + url = get_login_url(config->instance_url); + if (!url) { + err(1, NULL); return -1; } - req_data = json_dumps(root, 0); - json_decref(root); + req_data = get_login_req(email, password); if (!req_data) { - log_msg(LOG_WARNING, "register_app", "failed to dump json"); + free(url); return -1; } - size = strlen(instance_prefix) + strlen(app_register_url) + 1; - url = malloc(size); - if (!url) { - err(1, NULL); + resp_data = post_request(url, req_data); + free(url); + free(req_data); + if (!resp_data) { + log_msg(LOG_WARNING, "register_app", "login request failed"); return -1; } - strlcpy(url, instance_prefix, size); - strlcat(url, app_register_url, size); - - arg = calloc(1, sizeof(struct register_call_arg)); - if (!arg) { - err(1, NULL); + if (!resp_data) { + log_msg(LOG_WARNING, "register_app", "invalid response"); return -1; } - arg->url = url; - arg->domain = instance_prefix; - arg->post_data = req_data; - arg->callback = callback; - - return pthread_create(&t, NULL, ®ister_call, arg); -} - -struct login_call_arg { - char *url; - char *post_data; - void (*callback)(bool); -}; - -static void *login_call(void *req_arg) -{ - struct login_call_arg *arg = NULL; - char *resp = NULL; - json_t *root = NULL; - - arg = (struct login_call_arg *)req_arg; - if (!arg || !(arg->url)) { - log_msg(LOG_WARNING, "login_call", "invalid arguments"); - goto error; - } - - resp = post_request(arg->url, arg->post_data); - if (!resp) { - log_msg(LOG_WARNING, "login_call", "failed to send http request"); - goto error; - } + resp = get_login_resp(resp_data); + free(resp_data); if (!resp) { - log_msg(LOG_WARNING, "login_call", "null response"); - goto error; - } - - root = json_loads(resp, 0, NULL); - if (!root) { - log_msg(LOG_WARNING, "login_call", "failed to parse json"); - goto error; - } - - if (!json_is_object(root)) { - log_msg(LOG_WARNING, "login_call", "json root is not object"); - goto error; - } - - json_t *access_token_j = json_object_get(root, "access_token"); - json_t *scope_j = json_object_get(root, "scope"); - if (!json_is_string(access_token_j) || !json_is_string(scope_j) || - strlen(json_string_value(access_token_j)) < 1 || - strlen(json_string_value(scope_j)) < 1) { - log_msg(LOG_WARNING, "login_call", "invalid access_token or scope"); - goto error; + log_msg(LOG_WARNING, "register_app", "invalid response"); + return -1; } - config_set_access_token(json_string_value(access_token_j)); + config_set_access_token(resp->access_token); /* scope = strdup(json_string_value(scope_j)); */ if (config_save()) { log_msg(LOG_WARNING, "login_call", "failed to save config"); - goto error; } - free(arg->url); - if (arg->post_data) - free(arg->post_data); - free(arg); + free(resp->access_token); + free(resp->scope); free(resp); - json_decref(root); - (*(arg->callback))(true); - - printf("access_token: %s", config->access_token); - return NULL; - -error: - if (arg) { - if (arg->url) - free(arg->url); - if (arg->post_data) - free(arg->post_data); - free(arg); - } - if (resp) - free(resp); - if (root) - json_decref(root); - (*(arg->callback))(false); - return NULL; -} - -int login( - const char *email, const char *password, void (*callback)(bool success)) -{ - json_t *root; - json_error_t error; - pthread_t t; - size_t size; - char *url, *req_data; - struct login_call_arg *arg; - if (!email || strlen(email) < 1 || !password || strlen(password) < 1) { - log_msg(LOG_WARNING, "login", "invalid argument"); - return -1; - } - - if (!(config->client_id) || !*(config->client_id) || - !(config->client_secret) || !*(config->client_secret) || - !(config->instance_url) || !*(config->instance_url)) { - log_msg(LOG_WARNING, "login", "invalid client_id or client_secret"); - return -1; - } - - root = json_pack_ex(&error, 1, "{s:s, s:s, s:s, s:s, s:s}", "client_id", - config->client_id, "client_secret", config->client_secret, - "grant_type", "password", "username", email, "password", password); - - if (!root) { - log_msg(LOG_WARNING, "login", "json pack error: line %d: %s", - error.line, error.text); - return -1; - } - - req_data = json_dumps(root, 0); - json_decref(root); - if (!req_data) { - log_msg(LOG_WARNING, "login", "failed to dump json"); - return -1; - } - - size = strlen(config->instance_url) + strlen(login_url) + 1; - url = malloc(size); - if (!url) { - err(1, NULL); - return -1; - } - strlcpy(url, config->instance_url, size); - strlcat(url, login_url, size); - - arg = calloc(1, sizeof(struct login_call_arg)); - if (!arg) { - err(1, NULL); - return -1; - } - arg->url = url; - arg->post_data = req_data; - arg->callback = callback; - - return pthread_create(&t, NULL, &login_call, arg); + return 0; } diff --git a/src/auth.h b/src/auth.h index bc9c58b..9c3ee03 100644 --- a/src/auth.h +++ b/src/auth.h @@ -3,12 +3,7 @@ #include -bool is_logged_in(); -const char* get_access_token(); -const char* get_instance_url(); -int read_local_credentials(); -int register_app(const char *instance, void (*callback) (bool success)); -int login(const char *email, const char *password, void (*callback) (bool success)); +int login(const char *email, const char *password); #endif diff --git a/src/config.c b/src/config.c index ca2d48e..211e953 100644 --- a/src/config.c +++ b/src/config.c @@ -18,6 +18,44 @@ struct config _config; const struct config *config = (const struct config *)&_config; +int read_local_credentials() +{ + if (!(config->access_token) || !*(config->access_token) || + !(config->instance_url) || !*(config->instance_url)) { + log_msg(LOG_WARNING, "read_local_credentials", + "access_token not found"); + return -1; + } + return 0; +} + +bool is_logged_in() +{ + if (config->access_token) + return true; + return false; +} + +const char *get_access_token() +{ + return (const char *)config->access_token; +} + +const char *get_instance_url() +{ + return (const char *)config->instance_url; +} + +bool is_registered() +{ + if (!(_config.client_id) || !*(_config.client_id) || + !(_config.client_secret) || !*(_config.client_secret) || + !(_config.instance_url) || !*(_config.instance_url)) { + return false; + } + return true; +} + char *get_config_path() { char *home = getenv("HOME"); @@ -38,7 +76,7 @@ void config_load() char *path = get_config_path(); root = json_load_file(path, 0, &error); if (!root) { - log_msg(LOG_WARNING, "load_config", "json parse error: line %d: %s", + log_msg(LOG_WARNING, "config_load", "json parse error: line %d: %s", error.line, error.text); free(path); return; @@ -46,7 +84,7 @@ void config_load() free(path); if (!json_is_object(root)) { - log_msg(LOG_WARNING, "load_config", "json root is not object"); + log_msg(LOG_WARNING, "config_load", "json root is not object"); json_decref(root); return; } @@ -96,14 +134,14 @@ int config_save() if (stat(dir, &st)) { if (mkdir(dir, S_IRWXU)) { free(config_path); - log_msg(LOG_WARNING, "load_config", "failed to create config dir"); + log_msg(LOG_WARNING, "config_save", "failed to create config dir"); return -1; } } free(config_path); if (json_dump_file(root, get_config_path(), 0)) { - log_msg(LOG_WARNING, "load_config", "unable to save json"); + log_msg(LOG_WARNING, "config_save", "unable to save json"); json_decref(root); return -1; } diff --git a/src/config.h b/src/config.h index 3875a1b..2456c2b 100644 --- a/src/config.h +++ b/src/config.h @@ -1,6 +1,8 @@ #ifndef __CONFIG_H #define __CONFIG_H +#include + struct config { char *instance_url; char *client_id; @@ -9,6 +11,12 @@ struct config { }; extern const struct config *config; + +bool is_logged_in(); +const char* get_access_token(); +const char* get_instance_url(); +int read_local_credentials(); +bool is_registered(); void config_load(); int config_save(); void config_set_client_id(const char *cid); diff --git a/src/http.c b/src/http.c index 96a723d..84b62be 100644 --- a/src/http.c +++ b/src/http.c @@ -6,6 +6,7 @@ #include #include "string-util.h" #include "auth.h" +#include "config.h" #include "log.h" #define BUFFER_SIZE (256 * 1024) @@ -13,7 +14,11 @@ int http_init() { - return curl_global_init(CURL_GLOBAL_ALL); + if (curl_global_init(CURL_GLOBAL_ALL)) { + log_msg(LOG_WARNING, "http_init", "failed to init curl"); + return -1; + } + return 0; } void http_cleanup() @@ -44,7 +49,6 @@ static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream) char *get_request(const char *url) { - CURL *curl = NULL; CURLcode status; struct curl_slist *headers = NULL; @@ -61,6 +65,8 @@ char *get_request(const char *url) struct write_result write_result = {.data = data, .pos = 0}; + log_msg(LOG_INFO, "get_request", "loggedid: %d, token: %s", is_logged_in(), get_access_token()); + if (is_logged_in()) { const char *token = get_access_token(); size_t s = strlen(AUTH_HEADER_STR_PREFIX) + strlen(token) + 1; @@ -76,7 +82,7 @@ char *get_request(const char *url) curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result); status = curl_easy_perform(curl); - if (status != 0) { + if (status) { log_msg(LOG_WARNING, "get_request", "unable to request data from %s: %s", url, curl_easy_strerror(status)); @@ -145,7 +151,7 @@ char *post_request(const char *url, char *post_data) curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data); status = curl_easy_perform(curl); - if (status != 0) { + if (status) { log_msg(LOG_WARNING, "post_request", "unable to request data from %s: %s", url, curl_easy_strerror(status)); goto error; diff --git a/src/log.c b/src/log.c index 3c78caf..f1cb7d5 100644 --- a/src/log.c +++ b/src/log.c @@ -4,7 +4,7 @@ #include "log.h" static const char *log_levels[] = { - "info" + "info", "warning", "error", "fatal", @@ -18,7 +18,7 @@ void log_msg(enum log_level level, const char *namespace, const char *format, .. vfprintf(stderr, format, args); fprintf(stderr, "\n"); - if (level == LOG_WARNING) { + if (level == LOG_ERROR) { exit(EXIT_FAILURE); } else if (level == LOG_FATAL) { abort(); diff --git a/src/login_window.c b/src/login_window.c index f1823b9..a060d6c 100644 --- a/src/login_window.c +++ b/src/login_window.c @@ -5,6 +5,7 @@ #include #include #include "auth.h" +#include "register.h" #include "http.h" #include "timeline.h" #include "timeline_window.h" @@ -17,54 +18,51 @@ static GtkWidget *instance_name_box, *email_box, *password_box; static GtkWidget *submit_button; static GtkWidget *spinner; -static void window_removed( - GtkApplication *application, GtkWindow *w, gpointer user_data) +static gboolean login_completed(gpointer data) { - /* gtk_widget_destroy(GTK_WIDGET(w)); */ -} - -static void login_callback(bool success) -{ - if (!success) { + int *val_ptr = data; + gtk_spinner_stop(GTK_SPINNER(spinner)); + if (*val_ptr) { log_msg(LOG_WARNING, "login_callback", "login failed"); - gtk_spinner_stop(GTK_SPINNER(spinner)); - return; + } else { + create_timeline_window(application, NULL); } - gtk_spinner_stop(GTK_SPINNER(spinner)); - create_timeline_window(application, NULL); /* gtk_window_close(GTK_WINDOW(window)); */ /* gtk_application_remove_window( */ /* GTK_APPLICATION(application), GTK_WINDOW(window)); */ + + free(val_ptr); + return G_SOURCE_REMOVE; } -static void register_callback(bool success) +static gpointer user_register(gpointer data) { - if (!success) { - gtk_spinner_stop(GTK_SPINNER(spinner)); - log_msg(LOG_WARNING, "register_callback", "register failed"); - return; - } + int *val_ptr; + const char *instance_name, *email, *password; - const char *email, *password; + instance_name = gtk_entry_get_text(GTK_ENTRY(instance_name_box)); email = gtk_entry_get_text(GTK_ENTRY(email_box)); password = gtk_entry_get_text(GTK_ENTRY(password_box)); - if (login(email, password, &login_callback)) { - gtk_spinner_stop(GTK_SPINNER(spinner)); - return; + val_ptr = malloc(sizeof(int)); + *val_ptr = register_app(instance_name); + if (*val_ptr) { + log_msg(LOG_WARNING, "user_register", "registration failed"); + } + *val_ptr = login(email, password); + if (*val_ptr) { + log_msg(LOG_WARNING, "user_register", "login failed"); } + + gdk_threads_add_idle(login_completed, val_ptr); + return NULL; } static void submit_login_form() { gtk_spinner_start(GTK_SPINNER(spinner)); - const char *instance_name; - instance_name = gtk_entry_get_text(GTK_ENTRY(instance_name_box)); - if (register_app(instance_name, ®ister_callback)) { - gtk_spinner_stop(GTK_SPINNER(spinner)); - return; - } + g_thread_new("register", user_register, NULL); return; } @@ -104,7 +102,7 @@ void create_login_window(GtkApplication *app, gpointer user_data) gtk_window_set_title(GTK_WINDOW(window), "ap_client"); gtk_window_set_default_size(GTK_WINDOW(window), 800, 600); gtk_container_set_border_width(GTK_CONTAINER(window), 10); - g_signal_connect(app, "window-removed", G_CALLBACK(window_removed), NULL); + /* g_signal_connect(app, "window-removed", G_CALLBACK(window_removed), NULL); */ box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); gtk_widget_set_valign(GTK_WIDGET(box), GTK_ALIGN_CENTER); diff --git a/src/main.c b/src/main.c index c1c97f4..d95f547 100644 --- a/src/main.c +++ b/src/main.c @@ -23,8 +23,7 @@ static void startup(GtkApplication *app, gpointer user_data) static void activate(GtkApplication *app, gpointer user_data) { if (read_local_credentials()) { - /* create_login_window(app, user_data); */ - log_msg(LOG_WARNING, "activate", "failed to read config files"); + create_login_window(app, user_data); return; } create_timeline_window(app, NULL); diff --git a/src/register.c b/src/register.c new file mode 100644 index 0000000..4885ec5 --- /dev/null +++ b/src/register.c @@ -0,0 +1,128 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include "http.h" +#include "config.h" +#include "log.h" + +#define CLIENT_NAME "ap_client" +#define REGISTER_URL "https://%s/api/v1/apps" + +struct register_response { + char *client_id; + char *client_secret; +}; + +static char *get_register_url(const char *domain) +{ + char *url; + size_t size; + + size = strlen(REGISTER_URL) + strlen(domain) - 1; + url = malloc(size); + if (!url) { + err(1, NULL); + return NULL; + } + + sprintf(url, REGISTER_URL, domain); + return url; +} + +static char *get_register_req() +{ + char *req; + json_t *json_root; + + json_root = json_pack_ex(NULL, 1, "{s:s, s:s, s:s}", "client_name", + CLIENT_NAME, "redirect_uris", "urn:ietf:wg:oauth:2.0:oob", "scopes", + "read write push"); + if (!json_root) { + return NULL; + } + + req = json_dumps(json_root, 0); + json_decref(json_root); + + return req; +} + +static struct register_response *get_register_resp(const char *data) +{ + struct register_response *resp; + json_t *json_root; + json_t *cid, *csec; + + json_root = json_loads(data, 0, NULL); + if (!json_root) { + return NULL; + } + + if (!json_is_object(json_root)) { + json_decref(json_root); + return NULL; + } + + cid = json_object_get(json_root, "client_id"); + csec = json_object_get(json_root, "client_secret"); + if (!json_is_string(cid) || !json_is_string(csec)) { + json_decref(json_root); + return NULL; + } + + resp = calloc(1, sizeof(struct register_response)); + resp->client_id = strdup(json_string_value(cid)); + resp->client_secret = strdup(json_string_value(csec)); + + json_decref(json_root); + return resp; +} + +int register_app(const char *domain) +{ + char *url, *req_data, *resp_data; + struct register_response *resp; + + if (!domain || !*domain) { + log_msg(LOG_WARNING, "register_app", "invalid argument"); + return -1; + } + + url = get_register_url(domain); + if (!url) { + return -1; + } + + req_data = get_register_req(); + if (!req_data) { + free(url); + return -1; + } + + resp_data = post_request(url, req_data); + free(url); + free(req_data); + if (!resp_data) { + log_msg(LOG_WARNING, "register_app", "register request failed"); + return -1; + } + + resp = get_register_resp(resp_data); + free(resp_data); + if (!resp) { + log_msg(LOG_WARNING, "register_app", "invalid response"); + return -1; + } + + config_set_client_id(resp->client_id); + config_set_client_secret(resp->client_secret); + config_set_instance_url(domain); + + free(resp->client_id); + free(resp->client_secret); + free(resp); + + return 0; +} diff --git a/src/register.h b/src/register.h new file mode 100644 index 0000000..2122d6b --- /dev/null +++ b/src/register.h @@ -0,0 +1,8 @@ +#ifndef __REGISTER_H +#define __REGISTER_H + +int register_app(const char *domain); + +#endif + + diff --git a/src/string-util.c b/src/string-util.c index e183288..d5a702b 100644 --- a/src/string-util.c +++ b/src/string-util.c @@ -19,6 +19,7 @@ #include #include +#include /* * Copy src to string dst of size siz. At most siz-1 characters @@ -83,3 +84,40 @@ size_t strlcat(char *dst, const char *src, size_t siz) return (dlen + (s - src)); /* count does not include NUL */ } + +char *str_replace(const char *str, const char *old, const char *new) +{ + const char *ins; + char *result, *tmp; + size_t len_old, len_new, len_front, len_result; + int count; + + if (!str || !old || !new) + return NULL; + len_old = strlen(old); + if (len_old == 0) + return NULL; + len_new = strlen(new); + + ins = str; + for (count = 0; (tmp = strstr(ins, old)); ++count) { + ins = tmp + len_old; + } + + len_result = strlen(str) + (len_new - len_old) * count + 1; + tmp = result = malloc(len_result); + if (!result) + return NULL; + + while (count--) { + ins = strstr(str, old); + len_front = ins - str; + tmp = strncpy(tmp, str, len_front) + len_front; + strlcpy(tmp, new, len_result); + tmp += len_new; + str += len_front + len_old; + } + strlcpy(tmp, str, len_result); + return result; +} + diff --git a/src/string-util.h b/src/string-util.h index 5bf6f11..2625765 100644 --- a/src/string-util.h +++ b/src/string-util.h @@ -5,5 +5,6 @@ size_t strlcpy(char *dst, const char *src, size_t siz); size_t strlcat(char *dst, const char *src, size_t siz); +char *str_replace(const char *str, const char *old, const char *new); #endif diff --git a/src/timeline.c b/src/timeline.c index 753f9f1..be1a4c4 100644 --- a/src/timeline.c +++ b/src/timeline.c @@ -5,11 +5,28 @@ #include #include "timeline.h" #include "auth.h" +#include "config.h" #include "http.h" #include "string-util.h" #include "log.h" -static char *timeline_url = "/api/v1/timelines/home"; +#define TIMELINE_URL "https://%s/api/v1/timelines/home" + +static char *get_timeline_url(const char *domain) +{ + char *url; + size_t size; + + size = strlen(TIMELINE_URL) + strlen(domain) - 1; + url = malloc(size); + if (!url) { + err(1, NULL); + return NULL; + } + + sprintf(url, TIMELINE_URL, domain); + return url; +} struct timeline *timeline_from_json(const char *json_data) { @@ -77,19 +94,15 @@ struct timeline *get_timeline(const char *max_id, const char *since_id, const ch int limit) { char *url, *resp; - size_t size; struct timeline *t; - size = strlen(get_instance_url()) + strlen(timeline_url) + 1; - url = malloc(size); + url = get_timeline_url(config->instance_url); if (!url) { err(1, "get_timeline"); return NULL; } - strlcpy(url, get_instance_url(), size); - strlcat(url, timeline_url, size); - + printf("url: %s\n", url); resp = get_request(url); free(url); if (!resp) { diff --git a/src/timeline_window.c b/src/timeline_window.c index 516f2d6..4b17824 100644 --- a/src/timeline_window.c +++ b/src/timeline_window.c @@ -8,19 +8,74 @@ #include "http.h" #include "timeline.h" #include "log.h" +#include "string-util.h" -static GtkWidget *window, *spinner, *list_box; +static GtkWidget *window, *scrolled, *list_box; + +static gchar *html_to_pango(const char *content) +{ + char *str1, *str2; + gchar *result; + GRegex *regex = NULL; + GError *error = NULL; + + if (!content) { + return NULL; + } + + str1 = str_replace(content, "

", ""); + if (!str1) { + log_msg(LOG_WARNING, "html_to_pango", "failed to parse html"); + return NULL; + } + str2 = str_replace(str1, "

", ""); + free(str1); + if (!str2) { + log_msg(LOG_WARNING, "html_to_pango", "failed to parse html"); + return NULL; + } + + regex = g_regex_new("(class|target|rel)=\"(.|\n)*?\"", G_REGEX_CASELESS, 0, &error); + if (!regex) { + log_msg(LOG_WARNING, "html_to_pango", error->message); + g_free(error); + return NULL; + } + + result = g_regex_replace(regex, str2, -1, 0, "", 0, &error); + free(str2); + if (!result) { + log_msg(LOG_WARNING, "html_to_pango", error->message); + g_free(error); + return NULL; + } + + return result; +} static gboolean timeline_loaded(gpointer data) { struct timeline *t = data; - GtkWidget *label; for (size_t i = 0; i < t->size; i++) { - label = gtk_label_new(t->statuses[i]->content); - gtk_widget_show(GTK_WIDGET(label)); - gtk_list_box_prepend(GTK_LIST_BOX(list_box), GTK_WIDGET(label)); + GtkBuilder *post_builder; + GObject *post_box, *post_content_label; + char *content_markup; + + content_markup = html_to_pango(t->statuses[i]->content); + if (!content_markup) { + continue; + } + + post_builder = gtk_builder_new_from_file("data/post.ui"); + + post_box = gtk_builder_get_object(post_builder, "post_box"); + post_content_label = gtk_builder_get_object(post_builder, "content_text"); + gtk_label_set_markup(GTK_LABEL(post_content_label), content_markup); + + gtk_list_box_prepend(GTK_LIST_BOX(list_box), GTK_WIDGET(post_box)); + gtk_widget_show_all(GTK_WIDGET(post_box)); } - gtk_spinner_stop(GTK_SPINNER(spinner)); + /* gtk_spinner_stop(GTK_SPINNER(spinner)); */ timeline_free(t); return G_SOURCE_REMOVE; } @@ -44,17 +99,21 @@ void create_timeline_window(GtkApplication *app, gpointer user_data) gtk_window_set_title(GTK_WINDOW(window), "ap_client"); gtk_window_set_default_size(GTK_WINDOW(window), 800, 600); gtk_container_set_border_width(GTK_CONTAINER(window), 10); - gtk_window_set_destroy_with_parent(GTK_WINDOW(window), true); + + scrolled = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); list_box = gtk_list_box_new(); gtk_widget_set_valign(GTK_WIDGET(list_box), GTK_ALIGN_CENTER); gtk_widget_set_halign(GTK_WIDGET(list_box), GTK_ALIGN_CENTER); - spinner = gtk_spinner_new(); - gtk_spinner_start(GTK_SPINNER(spinner)); - gtk_container_add(GTK_CONTAINER(list_box), spinner); + /* spinner = gtk_spinner_new(); */ + /* gtk_spinner_start(GTK_SPINNER(spinner)); */ + /* gtk_container_add(GTK_CONTAINER(list_box), spinner); */ - gtk_container_add(GTK_CONTAINER(window), list_box); + gtk_container_add(GTK_CONTAINER(window), scrolled); + gtk_container_add(GTK_CONTAINER(scrolled), list_box); gtk_widget_show_all(window); g_thread_new("load_timeline_thread", &load_timeline, NULL); -- cgit v1.2.3