summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorSébastien Helleu <flashcode@flashtux.org>2023-09-08 11:34:56 +0200
committerSébastien Helleu <flashcode@flashtux.org>2023-09-16 10:19:51 +0200
commit63922ca03840caaa6deaf010d7627ad9542e19c5 (patch)
tree2c5f7c353e38907b66c88ca37adc01c88642b628 /src/core
parenta5f4c3770be0496bc7c8981ecd4adbe323831b4e (diff)
downloadweechat-63922ca03840caaa6deaf010d7627ad9542e19c5.zip
api: add function hook_url
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/hook/wee-hook-process.c4
-rw-r--r--src/core/hook/wee-hook-url.c419
-rw-r--r--src/core/hook/wee-hook-url.h59
-rw-r--r--src/core/wee-command.c25
-rw-r--r--src/core/wee-hook.c126
-rw-r--r--src/core/wee-hook.h2
-rw-r--r--src/core/wee-url.c127
-rw-r--r--src/core/wee-url.h4
9 files changed, 710 insertions, 57 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 0e8432748..0769999a4 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -72,6 +72,7 @@ set(LIB_CORE_SRC
hook/wee-hook-process.c hook/wee-hook-process.h
hook/wee-hook-signal.c hook/wee-hook-signal.h
hook/wee-hook-timer.c hook/wee-hook-timer.h
+ hook/wee-hook-url.c hook/wee-hook-url.h
)
# Check for flock support
diff --git a/src/core/hook/wee-hook-process.c b/src/core/hook/wee-hook-process.c
index 4de66b131..ff676dffc 100644
--- a/src/core/hook/wee-hook-process.c
+++ b/src/core/hook/wee-hook-process.c
@@ -278,7 +278,9 @@ hook_process_child (struct t_hook *hook_process)
{
ptr_url++;
}
- rc = weeurl_download (ptr_url, HOOK_PROCESS(hook_process, options));
+ rc = weeurl_download (ptr_url,
+ HOOK_PROCESS(hook_process, options),
+ NULL); /* output */
}
else if (strncmp (HOOK_PROCESS(hook_process, command), "func:", 5) == 0)
{
diff --git a/src/core/hook/wee-hook-url.c b/src/core/hook/wee-hook-url.c
new file mode 100644
index 000000000..6f00da4f2
--- /dev/null
+++ b/src/core/hook/wee-hook-url.c
@@ -0,0 +1,419 @@
+/*
+ * wee-hook-url.c - WeeChat URL hook
+ *
+ * Copyright (C) 2023 Sébastien Helleu <flashcode@flashtux.org>
+ *
+ * This file is part of WeeChat, the extensible chat client.
+ *
+ * WeeChat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * WeeChat is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with WeeChat. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include "../weechat.h"
+#include "../wee-hashtable.h"
+#include "../wee-hook.h"
+#include "../wee-infolist.h"
+#include "../wee-log.h"
+#include "../wee-string.h"
+#include "../wee-url.h"
+#include "../../gui/gui-chat.h"
+#include "../../plugins/plugin.h"
+
+
+/*
+ * Returns description of hook.
+ *
+ * Note: result must be freed after use.
+ */
+
+char *
+hook_url_get_description (struct t_hook *hook)
+{
+ char str_desc[1024];
+
+ snprintf (str_desc, sizeof (str_desc),
+ "URL: \"%s\", thread id: %d",
+ HOOK_URL(hook, url),
+ 0);
+
+ return strdup (str_desc);
+}
+
+/*
+ * Displays keys and values of a hashtable.
+ */
+
+void
+hook_url_hashtable_map_cb (void *data, struct t_hashtable *hashtable,
+ const void *key, const void *value)
+{
+ /* make C compiler happy */
+ (void) data;
+ (void) hashtable;
+
+ gui_chat_printf (NULL, " %s: \"%s\"",
+ (const char *)key,
+ (const char *)value);
+}
+
+/*
+ * Runs callback of url hook.
+ */
+
+void
+hook_url_run_callback (struct t_hook *hook)
+{
+ if (url_debug)
+ {
+ gui_chat_printf (NULL, "Running hook_url callback for URL \"%s\":",
+ HOOK_URL(hook, url));
+ gui_chat_printf (NULL, " options:");
+ hashtable_map (HOOK_URL(hook, options), &hook_url_hashtable_map_cb, NULL);
+ gui_chat_printf (NULL, " output:");
+ hashtable_map (HOOK_URL(hook, output), &hook_url_hashtable_map_cb, NULL);
+ }
+
+ (void) (HOOK_URL(hook, callback))
+ (hook->callback_pointer,
+ hook->callback_data,
+ HOOK_URL(hook, url),
+ HOOK_URL(hook, options),
+ HOOK_URL(hook, output));
+}
+
+/*
+ * Thread cleanup function: mark thread as not running any more.
+ */
+
+void
+hook_url_thread_cleanup (void *hook_pointer)
+{
+ struct t_hook *hook;
+
+ hook = (struct t_hook *)hook_pointer;
+
+ HOOK_URL(hook, thread_running) = 0;
+}
+
+/*
+ * URL transfer (in a separate thread).
+ */
+
+void *
+hook_url_transfer_thread (void *hook_pointer)
+{
+ struct t_hook *hook;
+
+ hook = (struct t_hook *)hook_pointer;
+
+ pthread_cleanup_push (&hook_url_thread_cleanup, hook);
+
+ (void) weeurl_download (HOOK_URL(hook, url),
+ HOOK_URL(hook, options),
+ HOOK_URL(hook, output));
+
+ pthread_cleanup_pop (1);
+
+ return NULL;
+}
+
+/*
+ * Checks if thread is still alive.
+ */
+
+int
+hook_url_timer_cb (const void *pointer, void *data, int remaining_calls)
+{
+ struct t_hook *hook;
+ const char *ptr_error;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) remaining_calls;
+
+ hook = (struct t_hook *)pointer;
+
+ if (hook->deleted)
+ return WEECHAT_RC_OK;
+
+ if (!HOOK_URL(hook, thread_running))
+ {
+ hook_url_run_callback (hook);
+ ptr_error = hashtable_get (HOOK_URL(hook, output), "error");
+ if (ptr_error && ptr_error[0])
+ {
+ gui_chat_printf (
+ NULL,
+ _("%sURL transfer error: %s (URL: \"%s\")"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ ptr_error,
+ HOOK_URL(hook, url));
+ }
+ unhook (hook);
+ return WEECHAT_RC_OK;
+ }
+
+ if (remaining_calls == 0)
+ {
+ hook_url_run_callback (hook);
+ if (weechat_debug_core >= 1)
+ {
+ gui_chat_printf (
+ NULL,
+ _("End of URL transfer '%s', timeout reached (%.1fs)"),
+ HOOK_URL(hook, url),
+ ((float)HOOK_URL(hook, timeout)) / 1000);
+ }
+ pthread_cancel (HOOK_URL(hook, thread_id));
+ usleep (1000);
+ unhook (hook);
+ }
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Starts transfer for an URL hook.
+ */
+
+void
+hook_url_transfer (struct t_hook *hook)
+{
+ int rc, timeout, max_calls;
+ long interval;
+
+ HOOK_URL(hook, thread_running) = 1;
+
+ /* create thread */
+ rc = pthread_create (&(HOOK_URL(hook, thread_id)), NULL,
+ &hook_url_transfer_thread, hook);
+ if (rc != 0)
+ {
+ gui_chat_printf (NULL,
+ _("%sError running thread in hook_url: %s (URL: \"%s\")"),
+ gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
+ strerror (rc),
+ HOOK_URL(hook, url));
+ unhook (hook);
+ return;
+ }
+
+ /* main thread */
+ timeout = HOOK_URL(hook, timeout);
+ interval = 100;
+ max_calls = 0;
+ if (timeout > 0)
+ {
+ if (timeout <= 100)
+ {
+ interval = timeout;
+ max_calls = 1;
+ }
+ else
+ {
+ interval = 100;
+ max_calls = timeout / 100;
+ if (timeout % 100 == 0)
+ max_calls++;
+ }
+ }
+ HOOK_URL(hook, hook_timer) = hook_timer (hook->plugin,
+ interval, 0, max_calls,
+ &hook_url_timer_cb,
+ hook,
+ NULL);
+}
+
+/*
+ * Hooks a URL.
+ *
+ * Returns pointer to new hook, NULL if error.
+ */
+
+struct t_hook *
+hook_url (struct t_weechat_plugin *plugin,
+ const char *url,
+ struct t_hashtable *options,
+ int timeout,
+ t_hook_callback_url *callback,
+ const void *callback_pointer,
+ void *callback_data)
+{
+ struct t_hook *new_hook;
+ struct t_hook_url *new_hook_url;
+
+ new_hook = NULL;
+ new_hook_url = NULL;
+
+ if (!url || !url[0] || !callback)
+ goto error;
+
+ new_hook = malloc (sizeof (*new_hook));
+ if (!new_hook)
+ goto error;
+
+ new_hook_url = malloc (sizeof (*new_hook_url));
+ if (!new_hook_url)
+ goto error;
+
+ hook_init_data (new_hook, plugin, HOOK_TYPE_URL, HOOK_PRIORITY_DEFAULT,
+ callback_pointer, callback_data);
+
+ new_hook->hook_data = new_hook_url;
+ new_hook_url->callback = callback;
+ new_hook_url->url = strdup (url);
+ new_hook_url->options = (options) ? hashtable_dup (options) : NULL;
+ new_hook_url->timeout = timeout;
+ new_hook_url->thread_id = 0;
+ new_hook_url->thread_running = 0;
+ new_hook_url->hook_timer = NULL;
+ new_hook_url->output = hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_STRING,
+ NULL, NULL);
+ hook_add_to_list (new_hook);
+
+ if (weechat_debug_core >= 1)
+ {
+ gui_chat_printf (NULL,
+ "debug: hook_url: url=\"%s\", "
+ "options=\"%s\", timeout=%d",
+ new_hook_url->url,
+ hashtable_get_string (new_hook_url->options,
+ "keys_values"),
+ new_hook_url->timeout);
+ }
+
+ hook_url_transfer (new_hook);
+
+ return new_hook;
+
+error:
+ if (new_hook)
+ free (new_hook);
+ if (new_hook_url)
+ free (new_hook_url);
+ return NULL;
+}
+
+/*
+ * Frees data in a url hook.
+ */
+
+void
+hook_url_free_data (struct t_hook *hook)
+{
+ if (!hook || !hook->hook_data)
+ return;
+
+ if (HOOK_URL(hook, url))
+ {
+ free (HOOK_URL(hook, url));
+ HOOK_URL(hook, url) = NULL;
+ }
+ if (HOOK_URL(hook, options))
+ {
+ hashtable_free (HOOK_URL(hook, options));
+ HOOK_URL(hook, options) = NULL;
+ }
+ if (HOOK_URL(hook, hook_timer))
+ {
+ unhook (HOOK_URL(hook, hook_timer));
+ HOOK_URL(hook, hook_timer) = NULL;
+ }
+ if (HOOK_URL(hook, thread_running))
+ {
+ pthread_cancel (HOOK_URL(hook, thread_id));
+ HOOK_URL(hook, thread_running) = 0;
+ }
+ if (HOOK_URL(hook, output))
+ {
+ hashtable_free (HOOK_URL(hook, output));
+ HOOK_URL(hook, output) = NULL;
+ }
+
+ free (hook->hook_data);
+ hook->hook_data = NULL;
+}
+
+/*
+ * Adds url hook data in the infolist item.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+hook_url_add_to_infolist (struct t_infolist_item *item,
+ struct t_hook *hook)
+{
+ if (!item || !hook || !hook->hook_data)
+ return 0;
+
+ if (!infolist_new_var_pointer (item, "callback", HOOK_URL(hook, callback)))
+ return 0;
+ if (!infolist_new_var_string (item, "url", HOOK_URL(hook, url)))
+ return 0;
+ if (!infolist_new_var_string (item, "options", hashtable_get_string (HOOK_URL(hook, options), "keys_values")))
+ return 0;
+ if (!infolist_new_var_integer (item, "timeout", (int)(HOOK_URL(hook, timeout))))
+ return 0;
+ if (!infolist_new_var_integer (item, "thread_running", (int)(HOOK_URL(hook, thread_running))))
+ return 0;
+ if (!infolist_new_var_pointer (item, "hook_timer", HOOK_URL(hook, hook_timer)))
+ return 0;
+ if (!infolist_new_var_string (item, "output", hashtable_get_string (HOOK_URL(hook, output), "keys_values")))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Prints url hook data in WeeChat log file (usually for crash dump).
+ */
+
+void
+hook_url_print_log (struct t_hook *hook)
+{
+ if (!hook || !hook->hook_data)
+ return;
+
+ log_printf (" url data:");
+ log_printf (" callback. . . . . . . : 0x%lx", HOOK_URL(hook, callback));
+ log_printf (" url . . . . . . . . . : '%s'", HOOK_URL(hook, url));
+ log_printf (" options . . . . . . . : 0x%lx (hashtable: '%s')",
+ HOOK_URL(hook, options),
+ hashtable_get_string (HOOK_URL(hook, options),
+ "keys_values"));
+ log_printf (" timeout . . . . . . . : %ld", HOOK_URL(hook, timeout));
+ log_printf (" thread_running. . . . : %d", (int)HOOK_URL(hook, thread_running));
+ log_printf (" hook_timer. . . . . . : 0x%lx", HOOK_URL(hook, hook_timer));
+ log_printf (" output. . . . . . . . : 0x%lx (hashtable: '%s')",
+ HOOK_URL(hook, output),
+ hashtable_get_string (HOOK_URL(hook, output),
+ "keys_values"));
+}
diff --git a/src/core/hook/wee-hook-url.h b/src/core/hook/wee-hook-url.h
new file mode 100644
index 000000000..1b306659b
--- /dev/null
+++ b/src/core/hook/wee-hook-url.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 Sébastien Helleu <flashcode@flashtux.org>
+ *
+ * This file is part of WeeChat, the extensible chat client.
+ *
+ * WeeChat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * WeeChat is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with WeeChat. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef WEECHAT_HOOK_URL_H
+#define WEECHAT_HOOK_URL_H
+
+struct t_weechat_plugin;
+struct t_infolist_item;
+struct t_hashtable;
+
+#define HOOK_URL(hook, var) (((struct t_hook_url *)hook->hook_data)->var)
+
+typedef int (t_hook_callback_url)(const void *pointer, void *data,
+ const char *url,
+ struct t_hashtable *options,
+ struct t_hashtable *output);
+
+struct t_hook_url
+{
+ t_hook_callback_url *callback; /* URL callback */
+ char *url; /* URL */
+ struct t_hashtable *options; /* URL options (see doc) */
+ long timeout; /* timeout (ms) (0 = no timeout) */
+ pthread_t thread_id; /* thread id */
+ int thread_running; /* 1 if thread is running */
+ struct t_hook *hook_timer; /* timer to check if thread has ended*/
+ struct t_hashtable *output; /* URL transfer output data */
+};
+
+extern char *hook_url_get_description (struct t_hook *hook);
+extern struct t_hook *hook_url (struct t_weechat_plugin *plugin,
+ const char *url,
+ struct t_hashtable *options,
+ int timeout,
+ t_hook_callback_url *callback,
+ const void *callback_pointer,
+ void *callback_data);
+extern void hook_url_free_data (struct t_hook *hook);
+extern int hook_url_add_to_infolist (struct t_infolist_item *item,
+ struct t_hook *hook);
+extern void hook_url_print_log (struct t_hook *hook);
+
+#endif /* WEECHAT_HOOK_URL_H */
diff --git a/src/core/wee-command.c b/src/core/wee-command.c
index 6269b945c..64fb1ea08 100644
--- a/src/core/wee-command.c
+++ b/src/core/wee-command.c
@@ -63,6 +63,7 @@
#include "wee-string.h"
#include "wee-sys.h"
#include "wee-upgrade.h"
+#include "wee-url.h"
#include "wee-utf8.h"
#include "wee-util.h"
#include "wee-version.h"
@@ -2223,6 +2224,15 @@ COMMAND_CALLBACK(debug)
return WEECHAT_RC_OK;
}
+ if (string_strcmp (argv[1], "url") == 0)
+ {
+ url_debug ^= 1;
+ gui_chat_printf (NULL,
+ _("Debug hook_url: %s"),
+ (url_debug) ? _("enabled") : _("disabled"));
+ return WEECHAT_RC_OK;
+ }
+
if (string_strcmp (argv[1], "windows") == 0)
{
debug_windows_tree ();
@@ -7056,14 +7066,17 @@ COMMAND_CALLBACK(upgrade)
}
/*
- * it is forbidden to upgrade while there are some background process
- * (hook type "process" or "connect")
+ * it is forbidden to upgrade while there are some background process or
+ * thread (hook types: process, connect, url)
*/
- if (weechat_hooks[HOOK_TYPE_PROCESS] || weechat_hooks[HOOK_TYPE_CONNECT])
+ if (weechat_hooks[HOOK_TYPE_PROCESS]
+ || weechat_hooks[HOOK_TYPE_CONNECT]
+ || weechat_hooks[HOOK_TYPE_URL])
{
gui_chat_printf (NULL,
_("%sCan't upgrade: there is one or more background "
- "process (hook type 'process' or 'connect')"),
+ "process/thread running (hook type: process, "
+ "connect or url)"),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR]);
return WEECHAT_RC_OK;
}
@@ -8189,7 +8202,7 @@ command_init ()
" || set <plugin> <level>"
" || dump|hooks [<plugin>]"
" || buffer|certs|color|dirs|infolists|libs|memory|tags|"
- "term|windows"
+ "term|url|windows"
" || callbacks <duration>[<unit>]"
" || mouse|cursor [verbose]"
" || hdata [free]"
@@ -8227,6 +8240,7 @@ command_init ()
" mouse: toggle debug for mouse\n"
" tags: display tags for lines\n"
" term: display infos about terminal\n"
+ " url: toggle debug for calls to hook_url (display output hashtable)\n"
" windows: display windows tree\n"
" time: measure time to execute a command or to send text to "
"the current buffer\n"
@@ -8256,6 +8270,7 @@ command_init ()
" || mouse verbose"
" || tags"
" || term"
+ " || url"
" || windows"
" || time %(commands:/)"
" || unicode",
diff --git a/src/core/wee-hook.c b/src/core/wee-hook.c
index b6bf36d96..758052a72 100644
--- a/src/core/wee-hook.c
+++ b/src/core/wee-hook.c
@@ -46,7 +46,7 @@
char *hook_type_string[HOOK_NUM_TYPES] =
{ "command", "command_run", "timer", "fd", "process", "connect", "line",
"print", "signal", "hsignal", "config", "completion", "modifier",
- "info", "info_hashtable", "infolist", "hdata", "focus" };
+ "info", "info_hashtable", "infolist", "hdata", "focus", "url" };
struct t_hook *weechat_hooks[HOOK_NUM_TYPES]; /* list of hooks */
struct t_hook *last_weechat_hook[HOOK_NUM_TYPES]; /* last hook */
int hooks_count[HOOK_NUM_TYPES]; /* number of hooks */
@@ -59,50 +59,98 @@ int hook_socketpair_ok = 0; /* 1 if socketpair() is OK */
/* hook callbacks */
t_callback_hook *hook_callback_add[HOOK_NUM_TYPES] =
{ NULL, NULL, NULL, &hook_fd_add_cb, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
t_callback_hook *hook_callback_remove[HOOK_NUM_TYPES] =
{ NULL, NULL, NULL, &hook_fd_remove_cb, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
t_callback_hook *hook_callback_free_data[HOOK_NUM_TYPES] =
-{ &hook_command_free_data, &hook_command_run_free_data,
- &hook_timer_free_data, &hook_fd_free_data,
- &hook_process_free_data, &hook_connect_free_data,
- &hook_line_free_data, &hook_print_free_data,
- &hook_signal_free_data, &hook_hsignal_free_data,
- &hook_config_free_data, &hook_completion_free_data,
- &hook_modifier_free_data, &hook_info_free_data,
- &hook_info_hashtable_free_data, &hook_infolist_free_data,
- &hook_hdata_free_data, &hook_focus_free_data };
+{
+ &hook_command_free_data,
+ &hook_command_run_free_data,
+ &hook_timer_free_data,
+ &hook_fd_free_data,
+ &hook_process_free_data,
+ &hook_connect_free_data,
+ &hook_line_free_data,
+ &hook_print_free_data,
+ &hook_signal_free_data,
+ &hook_hsignal_free_data,
+ &hook_config_free_data,
+ &hook_completion_free_data,
+ &hook_modifier_free_data,
+ &hook_info_free_data,
+ &hook_info_hashtable_free_data,
+ &hook_infolist_free_data,
+ &hook_hdata_free_data,
+ &hook_focus_free_data,
+ &hook_url_free_data,
+};
t_callback_hook_get_desc *hook_callback_get_desc[HOOK_NUM_TYPES] =
-{ &hook_command_get_description, &hook_command_run_get_description,
- &hook_timer_get_description, &hook_fd_get_description,
- &hook_process_get_description, &hook_connect_get_description,
- &hook_line_get_description, &hook_print_get_description,
- &hook_signal_get_description, &hook_hsignal_get_description,
- &hook_config_get_description, &hook_completion_get_description,
- &hook_modifier_get_description, &hook_info_get_description,
- &hook_info_hashtable_get_description, &hook_infolist_get_description,
- &hook_hdata_get_description, &hook_focus_get_description };
+{
+ &hook_command_get_description,
+ &hook_command_run_get_description,
+ &hook_timer_get_description,
+ &hook_fd_get_description,
+ &hook_process_get_description,
+ &hook_connect_get_description,
+ &hook_line_get_description,
+ &hook_print_get_description,
+ &hook_signal_get_description,
+ &hook_hsignal_get_description,
+ &hook_config_get_description,
+ &hook_completion_get_description,
+ &hook_modifier_get_description,
+ &hook_info_get_description,
+ &hook_info_hashtable_get_description,
+ &hook_infolist_get_description,
+ &hook_hdata_get_description,
+ &hook_focus_get_description,
+ &hook_url_get_description,
+};
t_callback_hook_infolist *hook_callback_add_to_infolist[HOOK_NUM_TYPES] =
-{ &hook_command_add_to_infolist, &hook_command_run_add_to_infolist,
- &hook_timer_add_to_infolist, &hook_fd_add_to_infolist,
- &hook_process_add_to_infolist, &hook_connect_add_to_infolist,
- &hook_line_add_to_infolist, &hook_print_add_to_infolist,
- &hook_signal_add_to_infolist, &hook_hsignal_add_to_infolist,
- &hook_config_add_to_infolist, &hook_completion_add_to_infolist,
- &hook_modifier_add_to_infolist, &hook_info_add_to_infolist,
- &hook_info_hashtable_add_to_infolist, &hook_infolist_add_to_infolist,
- &hook_hdata_add_to_infolist, &hook_focus_add_to_infolist };
+{
+ &hook_command_add_to_infolist,
+ &hook_command_run_add_to_infolist,
+ &hook_timer_add_to_infolist,
+ &hook_fd_add_to_infolist,
+ &hook_process_add_to_infolist,
+ &hook_connect_add_to_infolist,
+ &hook_line_add_to_infolist,
+ &hook_print_add_to_infolist,
+ &hook_signal_add_to_infolist,
+ &hook_hsignal_add_to_infolist,
+ &hook_config_add_to_infolist,
+ &hook_completion_add_to_infolist,
+ &hook_modifier_add_to_infolist,
+ &hook_info_add_to_infolist,
+ &hook_info_hashtable_add_to_infolist,
+ &hook_infolist_add_to_infolist,
+ &hook_hdata_add_to_infolist,
+ &hook_focus_add_to_infolist,
+ &hook_url_add_to_infolist,
+};
t_callback_hook *hook_callback_print_log[HOOK_NUM_TYPES] =
-{ &hook_command_print_log, &hook_command_run_print_log,
- &hook_timer_print_log, &hook_fd_print_log,
- &hook_process_print_log, &hook_connect_print_log,
- &hook_line_print_log, &hook_print_print_log,
- &hook_signal_print_log, &hook_hsignal_print_log,
- &hook_config_print_log, &hook_completion_print_log,
- &hook_modifier_print_log, &hook_info_print_log,
- &hook_info_hashtable_print_log, &hook_infolist_print_log,
- &hook_hdata_print_log, &hook_focus_print_log };
+{
+ &hook_command_print_log,
+ &hook_command_run_print_log,
+ &hook_timer_print_log,
+ &hook_fd_print_log,
+ &hook_process_print_log,
+ &hook_connect_print_log,
+ &hook_line_print_log,
+ &hook_print_print_log,
+ &hook_signal_print_log,
+ &hook_hsignal_print_log,
+ &hook_config_print_log,
+ &hook_completion_print_log,
+ &hook_modifier_print_log,
+ &hook_info_print_log,
+ &hook_info_hashtable_print_log,
+ &hook_infolist_print_log,
+ &hook_hdata_print_log,
+ &hook_focus_print_log,
+ &hook_url_print_log,
+};
/*
diff --git a/src/core/wee-hook.h b/src/core/wee-hook.h
index 04b17c921..bebb0419e 100644
--- a/src/core/wee-hook.h
+++ b/src/core/wee-hook.h
@@ -40,6 +40,7 @@ struct t_hook;
#include "hook/wee-hook-process.h"
#include "hook/wee-hook-signal.h"
#include "hook/wee-hook-timer.h"
+#include "hook/wee-hook-url.h"
struct t_gui_bar;
struct t_gui_buffer;
@@ -73,6 +74,7 @@ enum t_hook_type
HOOK_TYPE_INFOLIST, /* get some info as infolist */
HOOK_TYPE_HDATA, /* get hdata pointer */
HOOK_TYPE_FOCUS, /* focus event (mouse/key) */
+ HOOK_TYPE_URL, /* URL transfer */
/* number of hook types */
HOOK_NUM_TYPES,
};
diff --git a/src/core/wee-url.c b/src/core/wee-url.c
index 2c6be1f4f..7ec89b94f 100644
--- a/src/core/wee-url.c
+++ b/src/core/wee-url.c
@@ -44,6 +44,7 @@
{ #__name, CURLOPT_##__name, URL_TYPE_##__type, __constants }
+int url_debug = 0;
char *url_type_string[] = { "string", "long", "long long", "mask", "list" };
/*
@@ -1013,8 +1014,6 @@ struct t_url_option url_options[] =
{ NULL, 0, 0, NULL },
};
-char url_error[CURL_ERROR_SIZE + 1];
-
/*
* Searches for a constant in array of constants.
@@ -1116,7 +1115,7 @@ weeurl_search_option (const char *name)
*/
size_t
-weeurl_read (void *buffer, size_t size, size_t nmemb, void *stream)
+weeurl_read_stream (void *buffer, size_t size, size_t nmemb, void *stream)
{
return (stream) ? fread (buffer, size, nmemb, stream) : 0;
}
@@ -1126,12 +1125,27 @@ weeurl_read (void *buffer, size_t size, size_t nmemb, void *stream)
*/
size_t
-weeurl_write (void *buffer, size_t size, size_t nmemb, void *stream)
+weeurl_write_stream (void *buffer, size_t size, size_t nmemb, void *stream)
{
return (stream) ? fwrite (buffer, size, nmemb, stream) : 0;
}
/*
+ * Adds data to a dynamic string (callback called to catch stdout).
+ */
+
+size_t
+weeurl_write_string (void *buffer, size_t size, size_t nmemb, void *string)
+{
+ if (!string)
+ return 0;
+
+ string_dyn_concat ((char **)string, buffer, size * nmemb);
+
+ return size * nmemb;
+}
+
+/*
* Sets option in CURL easy handle (callback called for each option in hashtable
* "options").
*/
@@ -1303,6 +1317,17 @@ weeurl_set_proxy (CURL *curl, struct t_proxy *proxy)
/*
* Downloads URL using options.
*
+ * If output is not NULL, it must be a hashtable with keys and values of type
+ * "string". The following keys may be added in the hashtable,
+ * depending on the success or error of the URL transfer:
+ *
+ * key | description
+ * --------------|--------------------------------------------------------
+ * response_code | HTTP response code (as string)
+ * headers | HTTP headers in response
+ * output | stdout (set only if "file_out" was not set in options)
+ * error | error message (set only in case of error)
+ *
* Returns:
* 0: OK
* 1: invalid URL
@@ -1312,20 +1337,28 @@ weeurl_set_proxy (CURL *curl, struct t_proxy *proxy)
*/
int
-weeurl_download (const char *url, struct t_hashtable *options)
+weeurl_download (const char *url, struct t_hashtable *options,
+ struct t_hashtable *output)
{
CURL *curl;
struct t_url_file url_file[2];
char *url_file_option[2] = { "file_in", "file_out" };
char *url_file_mode[2] = { "rb", "wb" };
+ char url_error[CURL_ERROR_SIZE + 1], **string_headers, **string_output;
+ char str_response_code[32];
CURLoption url_file_opt_func[2] = { CURLOPT_READFUNCTION, CURLOPT_WRITEFUNCTION };
CURLoption url_file_opt_data[2] = { CURLOPT_READDATA, CURLOPT_WRITEDATA };
- void *url_file_opt_cb[2] = { &weeurl_read, &weeurl_write };
+ void *url_file_opt_cb[2] = { &weeurl_read_stream, &weeurl_write_stream };
struct t_proxy *ptr_proxy;
- int rc, curl_rc, i;
+ int rc, curl_rc, i, output_to_file;
+ long response_code;
rc = 0;
+ string_headers = NULL;
+ string_output = NULL;
+ url_error[0] = '\0';
+
for (i = 0; i < 2; i++)
{
url_file[i].filename = NULL;
@@ -1334,6 +1367,7 @@ weeurl_download (const char *url, struct t_hashtable *options)
if (!url || !url[0])
{
+ snprintf (url_error, sizeof (url_error), "%s", _("invalid URL"));
rc = 1;
goto end;
}
@@ -1341,6 +1375,7 @@ weeurl_download (const char *url, struct t_hashtable *options)
curl = curl_easy_init ();
if (!curl)
{
+ snprintf (url_error, sizeof (url_error), "%s", _("not enough memory"));
rc = 3;
goto end;
}
@@ -1358,7 +1393,19 @@ weeurl_download (const char *url, struct t_hashtable *options)
weeurl_set_proxy (curl, ptr_proxy);
}
+ /* set callback to retrieve HTTP headers */
+ if (output)
+ {
+ string_headers = string_dyn_alloc (1024);
+ if (string_headers)
+ {
+ curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, &weeurl_write_string);
+ curl_easy_setopt (curl, CURLOPT_HEADERDATA, string_headers);
+ }
+ }
+
/* set file in/out from options in hashtable */
+ output_to_file = 0;
if (options)
{
for (i = 0; i < 2; i++)
@@ -1369,15 +1416,33 @@ weeurl_download (const char *url, struct t_hashtable *options)
url_file[i].stream = fopen (url_file[i].filename, url_file_mode[i]);
if (!url_file[i].stream)
{
+ snprintf (url_error, sizeof (url_error),
+ (i == 0) ?
+ _("file \"%s\" not found") :
+ _("can not write file \"%s\""),
+ url_file[i].filename);
rc = 4;
goto end;
}
curl_easy_setopt (curl, url_file_opt_func[i], url_file_opt_cb[i]);
curl_easy_setopt (curl, url_file_opt_data[i], url_file[i].stream);
+ if (i == 1)
+ output_to_file = 1;
}
}
}
+ /* redirect stdout if no filename was given (via key "file_out") */
+ if (output && !output_to_file)
+ {
+ string_output = string_dyn_alloc (1024);
+ if (string_output)
+ {
+ curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, &weeurl_write_string);
+ curl_easy_setopt (curl, CURLOPT_WRITEDATA, string_output);
+ }
+ }
+
/* set other options in hashtable */
hashtable_map (options, &weeurl_option_map_cb, curl);
@@ -1386,11 +1451,36 @@ weeurl_download (const char *url, struct t_hashtable *options)
/* perform action! */
curl_rc = curl_easy_perform (curl);
- if (curl_rc != CURLE_OK)
+ if (curl_rc == CURLE_OK)
+ {
+ if (output)
+ {
+ curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &response_code);
+ snprintf (str_response_code, sizeof (str_response_code),
+ "%ld", response_code);
+ hashtable_set (output, "response_code", str_response_code);
+ }
+ }
+ else
{
- fprintf (stderr,
- _("curl error %d (%s) (URL: \"%s\")\n"),
- curl_rc, url_error, url);
+ if (output)
+ {
+ if (!url_error[0])
+ {
+ snprintf (url_error, sizeof (url_error),
+ "%s", _("transfer error"));
+ }
+ }
+ else
+ {
+ /*
+ * URL transfer done in a forked process: display error on stderr,
+ * which will be sent to the hook_process callback
+ */
+ fprintf (stderr,
+ _("curl error %d (%s) (URL: \"%s\")\n"),
+ curl_rc, url_error, url);
+ }
rc = 2;
}
@@ -1403,6 +1493,21 @@ end:
if (url_file[i].stream)
fclose (url_file[i].stream);
}
+ if (output)
+ {
+ if (string_headers)
+ {
+ hashtable_set (output, "headers", *string_headers);
+ string_dyn_free (string_headers, 1);
+ }
+ if (string_output)
+ {
+ hashtable_set (output, "output", *string_output);
+ string_dyn_free (string_output, 1);
+ }
+ if (url_error[0])
+ hashtable_set (output, "error", url_error);
+ }
return rc;
}
diff --git a/src/core/wee-url.h b/src/core/wee-url.h
index a70e29842..cfa8f6f00 100644
--- a/src/core/wee-url.h
+++ b/src/core/wee-url.h
@@ -54,10 +54,12 @@ struct t_url_file
FILE *stream; /* file stream */
};
+extern int url_debug;
extern char *url_type_string[];
extern struct t_url_option url_options[];
-extern int weeurl_download (const char *url, struct t_hashtable *options);
+extern int weeurl_download (const char *url, struct t_hashtable *options,
+ struct t_hashtable *output);
extern int weeurl_option_add_to_infolist (struct t_infolist *infolist,
struct t_url_option *option);