diff options
author | nirav <nirav@teisuu.com> | 2019-03-21 16:34:24 +0530 |
---|---|---|
committer | Dandelion <nirav@teisuu.com> | 2019-03-21 16:50:20 +0530 |
commit | 4b27c1a348d8de036dabd5525452f5b9ad08a32b (patch) | |
tree | f70c35351dc357d4297f985607fce9221ced96f0 /src | |
parent | c608bcc3dfab2abe7b66c10f8556086b2d45b3a3 (diff) | |
download | ap_client-4b27c1a348d8de036dabd5525452f5b9ad08a32b.tar.gz ap_client-4b27c1a348d8de036dabd5525452f5b9ad08a32b.zip |
Use g_threads, add builder ui for timeline
Diffstat (limited to 'src')
-rw-r--r-- | src/auth.c | 347 | ||||
-rw-r--r-- | src/auth.h | 7 | ||||
-rw-r--r-- | src/config.c | 46 | ||||
-rw-r--r-- | src/config.h | 8 | ||||
-rw-r--r-- | src/http.c | 14 | ||||
-rw-r--r-- | src/log.c | 4 | ||||
-rw-r--r-- | src/login_window.c | 56 | ||||
-rw-r--r-- | src/main.c | 3 | ||||
-rw-r--r-- | src/register.c | 128 | ||||
-rw-r--r-- | src/register.h | 8 | ||||
-rw-r--r-- | src/string-util.c | 38 | ||||
-rw-r--r-- | src/string-util.h | 1 | ||||
-rw-r--r-- | src/timeline.c | 27 | ||||
-rw-r--r-- | src/timeline_window.c | 81 |
14 files changed, 434 insertions, 334 deletions
@@ -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; } @@ -3,12 +3,7 @@ #include <stdbool.h> -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 <stdbool.h> + 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); @@ -6,6 +6,7 @@ #include <curl/curl.h> #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; @@ -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 <stdbool.h> #include <gtk-3.0/gtk/gtk.h> #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); @@ -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 <err.h> +#include <string.h> +#include <stdbool.h> +#include <jansson.h> +#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 <sys/types.h> #include <string.h> +#include <stdlib.h> /* * 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 <jansson.h> #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, "<p>", ""); + if (!str1) { + log_msg(LOG_WARNING, "html_to_pango", "failed to parse html"); + return NULL; + } + str2 = str_replace(str1, "</p>", ""); + 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); |